diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 8c230679..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,11 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - package-ecosystem: "nuget" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "weekly" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 65cd6d67..00000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,74 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ "main" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "main" ] - schedule: - - cron: '20 2 * * 3' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'csharp', 'javascript' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - with: - category: "/language:${{matrix.language}}" diff --git a/solutions/avnm-secured-hub-and-spoke/README.md b/solutions/avnm-secured-hub-and-spoke/README.md deleted file mode 100644 index f015932c..00000000 --- a/solutions/avnm-secured-hub-and-spoke/README.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -page_type: sample -languages: - - azurepowershell - - azurecli -products: - - azure - - azure-virtual-network - - virtual-network-manager -description: This sample deploys Azure virtual networks in a hub and spoke connectivity configuration, using Azure Virtual Network Manager to manage Virtual Network connectivity and implement sample Security Admin Rules. A VPN gateway and test VMs are included. -urlFragment: virtual-network-manager-secured-hub-and-spoke ---- - -# Secured hub and spoke deployment with Connected Groups - -This sample deploys Azure virtual networks in a hub and spoke configuration, using Azure Virtual Network Manager to manage Virtual Network connectivity and implement sample Security Admin Rules. A VPN Gateway and test VMs are deployed to complete the hub and spoke features. - -## Deploy sample - -### Step 1: Clone repository and navigate to the correct folder - -```bash -git clone https://github.com/mspnp/samples.git -cd ./samples/solutions/avnm-secured-hub-and-spoke/bicep -``` - -### Step 2: Deploy infrastructure and Virtual Network Manager resources - -```bash -# Generate ssh key and get public data. -ssh-keygen -t rsa -b 2048 - -az deployment sub create --location eastus --template-file main.bicep -n avnm-secured-hub-and-spoke --parameters sshKey="$(cat ~/.ssh/id_rsa.pub)" -``` - -## Solution deployment parameters - -| Parameter | Type | Description | Default | -| --------------- | ------------ | ------------------------------------- | -------------------------- | -| `adminUserName` | string | The admin user name for deployed VMs. | `admin-avnm` | -| `sshkey` | string | The user's public SSH key to be added to the Linux machines as part of the `ssh_authorized_keys` list | | - -## Step 4: Clean Up - -```bash -az group delete --name rg-hub-spoke-eastus --yes -``` - -## Microsoft Open Source Code of Conduct - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). - -Resources: - -- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) -- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) -- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns diff --git a/solutions/avnm-secured-hub-and-spoke/bicep/main.bicep b/solutions/avnm-secured-hub-and-spoke/bicep/main.bicep deleted file mode 100644 index 341e82e0..00000000 --- a/solutions/avnm-secured-hub-and-spoke/bicep/main.bicep +++ /dev/null @@ -1,117 +0,0 @@ -// deployment uses a 'subscription' target scope in order to create resource group and policy -targetScope = 'subscription' - -/*** PARAMETERS ***/ -@description('The user´s public SSH key that is added as authorized key to the Linux machines.') -param sshKey string - -@description('Username for the test VMs deployed in the spokes; default: admin-avnm') -param adminUsername string = 'admin-avnm' - -/*** VARIABLES ***/ - -var connectivityTopology = 'hubAndSpoke' -var networkGroupMembershipType = 'dynamic' -var location = deployment().location -var resourceGroupName = 'rg-hub-spoke-${location}' - -/*** RESOURCE GROUP ***/ -resource resourceGroup 'Microsoft.Resources/resourceGroups@2025-04-01' = { - name: resourceGroupName - location: location -} - -/*** RESOURCES (HUB) ***/ - -module hub 'modules/hub.bicep' = { - name: 'hub-resources-deployment-${location}' - scope: resourceGroup - params: { - } -} - -/*** RESOURCES (SPOKE) ***/ - -module spokeA 'modules/spoke.bicep' = { - name: 'spoke1-resources-deployment-${location}' - scope: resourceGroup - params: { - logAnalyticsWorkspaceId: hub.outputs.logAnalyticsWorkspaceId - spokeName: '001' - spokeVnetPrefix: '10.1.0.0/16' - sshKey: sshKey - adminUsername: adminUsername - routeTableId: hub.outputs.routeNextHopToFirewall - } -} - -module spokeB 'modules/spoke.bicep' = { - name: 'spoke2-resources-deployment-${location}' - scope: resourceGroup - params: { - logAnalyticsWorkspaceId: hub.outputs.logAnalyticsWorkspaceId - spokeName: '002' - spokeVnetPrefix: '10.2.0.0/16' - sshKey: sshKey - adminUsername: adminUsername - routeTableId: hub.outputs.routeNextHopToFirewall - } -} - -/*** Dynamic Membership Policy ***/ -module policy 'modules/dynMemberPolicy.bicep' = if (networkGroupMembershipType == 'dynamic') { - name: 'policy-${location}' - scope: subscription() - params: { - networkGroupId: avnm.outputs.networkGroupId - resourceGroupName: resourceGroupName - } -} - -/*** AZURE VIRTUAL NETWORK MANAGER RESOURCES ***/ -module avnm 'modules/avnm.bicep' = { - name: 'avnm-${location}' - scope: resourceGroup - params: { - hubVnetId: hub.outputs.hubVnetId - connectivityTopology: connectivityTopology - } -} - -// -// In order to deploy a Connectivity or Security configruation, the /commit endpoint must be called or a Deployment created in the Portal. -// This DeploymentScript resource executes a PowerShell script which calls the /commit endpoint and monitors the status of the deployment. -// -module deploymentScriptConnectivityConfigs 'modules/avnmDeploymentScript.bicep' = { - name: 'ds-connectivityconfigs-${location}' - scope: resourceGroup - dependsOn: [ - policy - ] - params: { - userAssignedIdentityId: avnm.outputs.userAssignedIdentityId - configurationId: avnm.outputs.connectivityConfigurationId - configType: 'Connectivity' - networkManagerName: avnm.outputs.networkManagerName - deploymentScriptName: 'ds-${location}-connectivityconfigs' - } -} - -module deploymentScriptSecurityConfigs 'modules/avnmDeploymentScript.bicep' = { - name: 'ds-securityadminconfigs-${location}' - scope: resourceGroup - dependsOn: [ - policy - ] - params: { - userAssignedIdentityId: avnm.outputs.userAssignedIdentityId - configurationId: avnm.outputs.securtyAdminConfigurationId - configType: 'SecurityAdmin' - networkManagerName: avnm.outputs.networkManagerName - deploymentScriptName: 'ds-${location}-securityadminconfigs' - } -} - -// output policy resource ids to facilitate cleanup -output policyDefinitionId string = policy.outputs.policyDefinitionId ?? 'not_deployed' -output policyAssignmentId string = policy.outputs.policyAssignmentId ?? 'not_deployed' diff --git a/solutions/avnm-secured-hub-and-spoke/bicep/modules/avnm.bicep b/solutions/avnm-secured-hub-and-spoke/bicep/modules/avnm.bicep deleted file mode 100644 index 17b16fb8..00000000 --- a/solutions/avnm-secured-hub-and-spoke/bicep/modules/avnm.bicep +++ /dev/null @@ -1,147 +0,0 @@ -/*** PARAMETERS ***/ -@description('All resources will be deployed to this region.') -param location string = resourceGroup().location - -@description('Hub VNet ID to which the spokes will connect.') -param hubVnetId string - -@description('Connectivity topology to be used for networkManagers Configuration.') -param connectivityTopology string - -/*** RESOURCES ***/ - -@description('This is the Azure Virtual Network Manager which will be used to implement the connected group for spoke-to-spoke connectivity.') -resource vnm 'Microsoft.Network/networkManagers@2024-05-01' = { - name: 'vnm-${location}' - location: location - properties: { - networkManagerScopeAccesses: [ - 'Connectivity' - 'SecurityAdmin' - ] - networkManagerScopes: { - subscriptions: [ - '/subscriptions/${subscription().subscriptionId}' - ] - managementGroups: [] - } - } -} - -@description('This is the dynamic group for spoke VNETs.') -resource vnmNetworkGroupSpokesDynamic 'Microsoft.Network/networkManagers/networkGroups@2024-05-01' = { - name: 'vnm-ng-learn-prod-${location}-dynamic-001' - parent: vnm - properties: { - description: 'Network Group - Dynamic' - } -} - -// Connectivity Topology: hub and spoke -// -// Spoke 'A' VM Effective routes -// Source State Address Prefix Next Hop Type Next Hop IP -// -------- ------- --------------------------- --------------- ------------- -// Default Active 10.100.0.0/22 VnetLocal -// Default Active 10.0.0.0/22 VNetPeering -// Default Active 0.0.0.0/0 Internet -// ... -@description('This connectivity configuration defines the connectivity between the spokes using Hub and Spoke - traffic flow through hub requires an NVA to route it.') -resource vnmConnectivityConfigurationHubAndSpoke 'Microsoft.Network/networkManagers/connectivityConfigurations@2024-05-01' = if (connectivityTopology == 'hubAndSpoke') { - name: 'vnm-cc-learn-prod-${location}-001' - parent: vnm - properties: { - description: 'Spoke-to-spoke connectivity configuration' - appliesToGroups: [ - { - networkGroupId: vnmNetworkGroupSpokesDynamic.id - isGlobal: 'False' - useHubGateway: 'True' - groupConnectivity: 'None' - } - ] - connectivityTopology: 'HubAndSpoke' - deleteExistingPeering: 'True' - hubs: [ - { - resourceId: hubVnetId - resourceType: 'Microsoft.Network/virtualNetworks' - } - ] - isGlobal: 'False' - } -} - -@description('This is the securityadmin configuration assigned to the AVNM') -resource vnmSecurityConfig 'Microsoft.Network/networkManagers/securityAdminConfigurations@2024-05-01' = { - name: 'vnm-sac-learn-prod-${location}-001' - parent: vnm - properties: { - applyOnNetworkIntentPolicyBasedServices: [ 'None' ] - description: 'Security Group for AVNM' - } -} - -@description('This is the rules collection for the security admin config assigned to the AVNM') -resource vnmRulesCollection 'Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections@2024-05-01' = { - name: 'vnm-rc-learn-prod-${location}-001' - parent: vnmSecurityConfig - properties: { - appliesToGroups: [ - { - networkGroupId: vnmNetworkGroupSpokesDynamic.id - } - ] - } -} - -@description('This example rule denies outbound HTTP/S traffic to the internet') -resource DENY_INTERNET_HTTP_HTTPS 'Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules@2024-05-01' = { - name: 'DENY_INTERNET_HTTP_HTTPS' - kind: 'Custom' - parent: vnmRulesCollection - properties: { - access: 'Deny' - description: 'This rule blocks traffic to the internet on HTTP and HTTPS' - destinationPortRanges: [ '80','443' ] - destinations: [ - { - addressPrefix: '*' - addressPrefixType: 'IPPrefix' - } - ] - direction: 'Inbound' - priority: 100 - protocol: 'TCP' - sourcePortRanges: [ '0-65535' ] - sources: [ - { - addressPrefix: '*' - addressPrefixType: 'IPPrefix' - } - ] - } -} - - -@description('This user assigned identity is used by the Deployment Script resource to interact with Azure resources.') -resource id 'Microsoft.ManagedIdentity/userAssignedIdentities@2025-01-31-preview' = { - name: 'id-${location}' - location: location -} - -@description('This role assignment grants the user assigned identity the Contributor role on the resource group.') -resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid(resourceGroup().id, id.name) - properties: { - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') // Contributor: b24988ac-6180-42a0-ab88-20f7382dd24c - principalId: id.properties.principalId - principalType: 'ServicePrincipal' - } -} - -output networkManagerName string = vnm.name -output userAssignedIdentityId string = id.id -output connectivityConfigurationId string = vnmConnectivityConfigurationHubAndSpoke.id -output securtyAdminConfigurationId string = vnmSecurityConfig.id -output networkGroupId string = vnmNetworkGroupSpokesDynamic.id diff --git a/solutions/avnm-secured-hub-and-spoke/bicep/modules/avnmDeploymentScript.bicep b/solutions/avnm-secured-hub-and-spoke/bicep/modules/avnmDeploymentScript.bicep deleted file mode 100644 index c713692c..00000000 --- a/solutions/avnm-secured-hub-and-spoke/bicep/modules/avnmDeploymentScript.bicep +++ /dev/null @@ -1,88 +0,0 @@ -/*** PARAMETERS ***/ -@description('All resources will be deployed to this region.') -param location string = resourceGroup().location - -@description('The user-assigned identity to be used by the deployment script.') -param userAssignedIdentityId string - -@description('The name of the Azure Network Manager resource.') -param networkManagerName string - -@description('Configuration ID of the Network Manager connectivity configuration.') -param configurationId string - -@description('Deployment script name. Must be unique within the resource group.') -param deploymentScriptName string -@allowed([ - 'Connectivity' - 'SecurityAdmin' -]) - -@description('The type of configuration to deploy.') -param configType string - -/*** RESOURCES ***/ - -// the commit action is idempotent, so re-running the deployment will not cause any issues -@description('Create a Deployment Script resource to perform the commit/deployment of the Network Manager connectivity configuration.') -resource deploymentScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = { - name: deploymentScriptName - location: location - kind: 'AzurePowerShell' - identity: { - type: 'UserAssigned' - userAssignedIdentities: { - '${userAssignedIdentityId}': {} - } - } - properties: { - azPowerShellVersion: '8.3' - retentionInterval: 'PT1H' - timeout: 'PT1H' - arguments: '-networkManagerName "${networkManagerName}" -targetLocations ${location} -configIds ${configurationId} -subscriptionId ${subscription().subscriptionId} -configType ${configType} -resourceGroupName ${resourceGroup().name}' - scriptContent: ''' - param ( - # AVNM subscription id - [parameter(mandatory=$true)][string]$subscriptionId, - - # AVNM resource name - [parameter(mandatory=$true)][string]$networkManagerName, - - # string with comma-separated list of config ids to deploy. ids must be of the same config type - [parameter(mandatory=$true)][string[]]$configIds, - - # string with comma-separated list of deployment target regions - [parameter(mandatory=$true)][string[]]$targetLocations, - - # configuration type to deploy. must be either connecticity or securityadmin - [parameter(mandatory=$true)][ValidateSet('Connectivity','SecurityAdmin')][string]$configType, - - # AVNM resource group name - [parameter(mandatory=$true)][string]$resourceGroupName - ) - - $null = Login-AzAccount -Identity -Subscription $subscriptionId - - [System.Collections.Generic.List[string]]$configIdList = @() - $configIdList.addRange($configIds) - [System.Collections.Generic.List[string]]$targetLocationList = @() # target locations for deployment - $targetLocationList.addRange($targetLocations) - - $deployment = @{ - Name = $networkManagerName - ResourceGroupName = $resourceGroupName - ConfigurationId = $configIdList - TargetLocation = $targetLocationList - CommitType = $configType - } - - try { - Deploy-AzNetworkManagerCommit @deployment -ErrorAction Stop - } - catch { - Write-Error "Deployment failed with error: $_" - exit 1 - } - ''' - } -} diff --git a/solutions/avnm-secured-hub-and-spoke/bicep/modules/dynMemberPolicy.bicep b/solutions/avnm-secured-hub-and-spoke/bicep/modules/dynMemberPolicy.bicep deleted file mode 100644 index 66b8ad24..00000000 --- a/solutions/avnm-secured-hub-and-spoke/bicep/modules/dynMemberPolicy.bicep +++ /dev/null @@ -1,61 +0,0 @@ -targetScope = 'subscription' - -/*** PARAMETERS ***/ -@description('Network group ID to which the policy will be assigned') -param networkGroupId string -@description('Resource group name for filtering the policy assignment') -param resourceGroupName string - -/*** RESOURCES ***/ - -@description('This is a Policy definition for dyanamic group membership') -resource policyDefinition 'Microsoft.Authorization/policyDefinitions@2025-01-01' = { - name: uniqueString(networkGroupId) - properties: { - description: 'AVNM quickstart dynamic group membership Policy' - displayName: 'AVNM quickstart dynamic group membership Policy' - mode: 'Microsoft.Network.Data' - policyRule: { - if: { - allof: [ - { - field: 'type' - equals: 'Microsoft.Network/virtualNetworks' - } - { - // virtual networks must have -prod in the name - field: 'name' - contains: '-prod' - } - { - // virtual network ids must include this sample's resource group ID - limiting the chance that dynamic membership impacts other vnets in your subscriptions - field: 'id' - like: '${subscription().id}/resourcegroups/${resourceGroupName}/*' - } - ] - } - then: { - // 'addToNetworkGroup' is a special effect used by AVNM network groups - effect: 'addToNetworkGroup' - details: { - networkGroupId: networkGroupId - } - } - } - } -} - -// once assigned, the policy will evaluate as new VNETs are created and on a special evaluation cycle for AVNM, enabling quick dynamic group updates -@description('Assigns above policy for dynamic group membership') -resource policyAssignment 'Microsoft.Authorization/policyAssignments@2025-01-01' = { - name: uniqueString(networkGroupId) - properties: { - description: 'AVNM quickstart dynamic group membership Policy' - displayName: 'AVNM quickstart dynamic group membership Policy' - enforcementMode: 'Default' - policyDefinitionId: policyDefinition.id - } -} - -output policyDefinitionId string = policyDefinition.id -output policyAssignmentId string = policyAssignment.id diff --git a/solutions/avnm-secured-hub-and-spoke/bicep/modules/hub.bicep b/solutions/avnm-secured-hub-and-spoke/bicep/modules/hub.bicep deleted file mode 100644 index b6d23162..00000000 --- a/solutions/avnm-secured-hub-and-spoke/bicep/modules/hub.bicep +++ /dev/null @@ -1,401 +0,0 @@ -/*** PARAMETERS ***/ -@description('The location where hub resources will deployed. It includes a Log Analytics Workspace, a VNet, and a VPN Gateway.') -param location string = resourceGroup().location - -/*** RESOURCES ***/ - -@description('This Log Analyics Workspace stores logs from the regional hub network, its spokes, and other related resources. Workspaces are regional resource, as such there would be one workspace per hub (region)') -resource laHub 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { - name: 'la-hub-${location}' - location: location - properties: { - sku: { - name: 'PerGB2018' - } - retentionInDays: 90 - forceCmkForQuery: false - publicNetworkAccessForIngestion: 'Enabled' - publicNetworkAccessForQuery: 'Enabled' - features: { - disableLocalAuth: false - enableLogAccessUsingOnlyResourcePermissions: true - } - workspaceCapping: { - dailyQuotaGb: -1 - } - } -} - -resource laHub_diagnosticsSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { - name: 'to-hub-la' - scope: laHub - properties: { - workspaceId: laHub.id - logs: [ - { - categoryGroup: 'allLogs' - enabled: true - } - ] - metrics: [ - { - category: 'AllMetrics' - enabled: true - } - ] - } -} - -@description('The regional hub network.') -resource vnetHub 'Microsoft.Network/virtualNetworks@2024-05-01' = { - name: 'vnet-learn-hub-${location}-001' - location: location - properties: { - addressSpace: { - addressPrefixes: [ - '10.0.0.0/16' - ] - } - subnets: [ - { - name: 'AzureFirewallSubnet' - properties: { - addressPrefix: '10.0.3.0/26' - defaultOutboundAccess: false - } - } - { - name: 'AzureFirewallManagementSubnet' - properties: { - addressPrefix: '10.0.3.64/26' - defaultOutboundAccess: false - } - } - { - name: 'default' - properties: { - addressPrefix: '10.0.3.128/25' - defaultOutboundAccess: false - } - } - ] - } - resource azureFirewallSubnet 'subnets' existing = { - name: 'AzureFirewallSubnet' - } -} - -resource vnetHub_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { - name: 'to-hub-la' - scope: vnetHub - properties: { - workspaceId: laHub.id - metrics: [ - { - category: 'AllMetrics' - enabled: true - } - ] - } -} - -resource snetGateway 'Microsoft.Network/virtualNetworks/subnets@2024-05-01' = { - name: 'GatewaySubnet' - parent: vnetHub - properties: { - addressPrefix: '10.0.2.0/27' - } -} - -@description('The public IPs for the regional VPN gateway.') -resource pipVpnGateway 'Microsoft.Network/publicIPAddresses@2024-05-01' = { - name: 'pip-learn-hub-${location}-vngw001' - location: location - sku: { - name: 'Standard' - } - zones: [ - '1' - '2' - '3' - ] - properties: { - publicIPAllocationMethod: 'Static' - idleTimeoutInMinutes: 4 - publicIPAddressVersion: 'IPv4' - } -} - -resource pipVpnGateway_diagnosticSetting 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { - name: 'to-hub-la' - scope: pipVpnGateway - properties: { - workspaceId: laHub.id - logs: [ - { - categoryGroup: 'allLogs' - enabled: true - } - ] - metrics: [ - { - category: 'AllMetrics' - enabled: true - } - ] - } -} - -@description('The is the regional VPN gateway, configured with basic settings.') -resource vgwHub 'Microsoft.Network/virtualNetworkGateways@2024-05-01' = { - name: 'vgw-learn-hub-${location}-001' - location: location - properties: { - sku: { - name: 'VpnGw2AZ' - tier: 'VpnGw2AZ' - } - gatewayType: 'Vpn' - vpnType: 'RouteBased' - vpnGatewayGeneration: 'Generation2' - ipConfigurations: [ - { - name: 'default' - properties: { - privateIPAllocationMethod: 'Dynamic' - publicIPAddress: { - id: pipVpnGateway.id - } - subnet: { - id: snetGateway.id - } - } - } - ] - } -} - -resource vgwHub_diagnosticSetting 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { - name: 'to-hub-la' - scope: vgwHub - properties: { - workspaceId: laHub.id - logs: [ - { - categoryGroup: 'allLogs' - enabled: true - } - ] - metrics: [ - { - category: 'AllMetrics' - enabled: true - } - ] - } -} - -// Allocate three IP addresses to the firewall -var numFirewallIpAddressesToAssign = 1 -resource pipsAzureFirewall 'Microsoft.Network/publicIPAddresses@2024-05-01' = [for i in range(0, numFirewallIpAddressesToAssign): { - name: 'pip-fw-${location}-${padLeft(i, 2, '0')}' - location: location - sku: { - name: 'Standard' - } - zones: [ - '1' - '2' - '3' - ] - properties: { - publicIPAllocationMethod: 'Static' - idleTimeoutInMinutes: 4 - publicIPAddressVersion: 'IPv4' - } -}] - -resource pipsAzureFirewall_diagnosticSetting 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [for i in range(0, numFirewallIpAddressesToAssign): { - name: 'to-hub-la' - scope: pipsAzureFirewall[i] - properties: { - workspaceId: laHub.id - logs: [ - { - categoryGroup: 'allLogs' - enabled: true - } - ] - metrics: [ - { - category: 'AllMetrics' - enabled: true - } - ] - } -}] - -@description('Azure Firewall Policy') -resource fwPolicy 'Microsoft.Network/firewallPolicies@2024-05-01' = { - name: 'fw-policies-${location}' - location: location - properties: { - sku: { - tier: 'Basic' - } - insights: { - isEnabled: true - retentionDays: 30 - logAnalyticsResources: { - defaultWorkspaceId: { - id: laHub.id - } - } - } - intrusionDetection: null // Only valid on Premium tier sku - } - - // This network hub starts out with only supporting external DNS queries. This is only being done for - // simplicity in this deployment and is not guidance, please ensure all firewall rules are aligned with - // your security standards. - resource defaultNetworkRuleCollectionGroup 'ruleCollectionGroups@2024-05-01' = { - name: 'DefaultNetworkRuleCollectionGroup' - properties: { - priority: 200 - ruleCollections: [ - { - ruleCollectionType: 'FirewallPolicyFilterRuleCollection' - name: 'org-wide-allowed' - priority: 100 - action: { - type: 'Allow' - } - rules: [ - { - ruleType: 'NetworkRule' - name: 'DNS' - description: 'Allow DNS outbound (for simplicity, adjust as needed)' - ipProtocols: [ - 'UDP' - ] - sourceAddresses: [ - '*' - ] - sourceIpGroups: [] - destinationAddresses: [ - '*' - ] - destinationIpGroups: [] - destinationFqdns: [] - destinationPorts: [ - '53' - ] - } - ] - } - ] - } - } - - // Network hub starts out with no allowances for appliction rules - resource defaultApplicationRuleCollectionGroup 'ruleCollectionGroups@2024-05-01' = { - name: 'DefaultApplicationRuleCollectionGroup' - dependsOn: [ - defaultNetworkRuleCollectionGroup - ] - properties: { - priority: 300 - ruleCollections: [ - { - ruleCollectionType: 'FirewallPolicyFilterRuleCollection' - name: 'org-wide-allowed' - priority: 100 - action: { - type: 'Allow' - } - rules: [] - } - ] - } - } -} - -@description('This is the regional Azure Firewall that all regional spoke networks can egress through.') -resource fwHub 'Microsoft.Network/azureFirewalls@2024-05-01' = { - name: 'fw-${location}' - location: location - zones: [ - '1' - '2' - '3' - ] - dependsOn: [ - // This helps prevent multiple PUT updates happening to the firewall causing a CONFLICT race condition - // Ref: https://learn.microsoft.com/azure/firewall-manager/quick-firewall-policy - fwPolicy::defaultApplicationRuleCollectionGroup - fwPolicy::defaultNetworkRuleCollectionGroup - ] - properties: { - sku: { - name: 'AZFW_VNet' - tier: 'Standard' - } - firewallPolicy: { - id: fwPolicy.id - } - ipConfigurations: [for i in range(0, numFirewallIpAddressesToAssign): { - name: pipsAzureFirewall[i].name - properties: { - subnet: (0 == i) ? { - id: vnetHub::azureFirewallSubnet.id - } : null - publicIPAddress: { - id: pipsAzureFirewall[i].id - } - } - }] - } -} - -resource fwHub_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { - name: 'to-hub-la' - scope: fwHub - properties: { - workspaceId: laHub.id - logs: [ - { - categoryGroup: 'allLogs' - enabled: true - } - ] - metrics: [ - { - category: 'AllMetrics' - enabled: true - } - ] - } -} - -@description('Next hop to the regional hub\'s Azure Firewall') -resource rt_nextHopToFirewall 'Microsoft.Network/routeTables@2024-05-01' = { - name: 'rt-to-hub-fw-${location}' - location: location - properties: { - routes: [ - { - name: 'r-nexthop-to-fw' - properties: { - nextHopType: 'VirtualAppliance' - addressPrefix: '0.0.0.0/0' - nextHopIpAddress: fwHub.properties.ipConfigurations[0].properties.privateIPAddress - } - } - ] - } -} - -output hubVnetId string = vnetHub.id -output logAnalyticsWorkspaceId string = laHub.id -output firewall object = fwHub -output routeNextHopToFirewall string = rt_nextHopToFirewall.id diff --git a/solutions/avnm-secured-hub-and-spoke/bicep/modules/spoke.bicep b/solutions/avnm-secured-hub-and-spoke/bicep/modules/spoke.bicep deleted file mode 100644 index 5906feab..00000000 --- a/solutions/avnm-secured-hub-and-spoke/bicep/modules/spoke.bicep +++ /dev/null @@ -1,190 +0,0 @@ -/*** PARAMETERS ***/ - -@description('All resources will be deployed to this region.') -param location string = resourceGroup().location - -@description('Short name to identify the spoke.') -param spokeName string - -@description('VNet address prefix for the spoke.') -param spokeVnetPrefix string - -@description('The Log Analytics Workspace ID to which the spoke will send its logs.') -param logAnalyticsWorkspaceId string - -@description('The public SSH key to be used for the VM in the spoke.') -param sshKey string - -@description('Username for the test VMs deployed in the spokes; default: admin-avnm') -param adminUsername string = 'admin-avnm' - -@description('The route table ID to define the next hop for the spoke.') -param routeTableId string - -/*** RESOURCES ***/ - -resource vnet 'Microsoft.Network/virtualNetworks@2024-05-01' = { - name: 'vnet-learn-prod-${location}-${toLower(spokeName)}' - location: location - properties: { - addressSpace: { - addressPrefixes: [ - spokeVnetPrefix - ] - } - subnets: [ - { - name: 'default' - properties: { - addressPrefix: replace(spokeVnetPrefix, '.0.0/22', '.1.0/24') - defaultOutboundAccess: false - routeTable: { - id: routeTableId - } - } - } - ] - } -} - -resource vnet_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { - scope: vnet - name: 'vnet-to-hub-la' - properties: { - workspaceId: logAnalyticsWorkspaceId - metrics: [ - { - category: 'AllMetrics' - enabled: true - } - ] - } -} - -@description('The private Network Interface Card for the Windows VM in spoke.') -resource nic 'Microsoft.Network/networkInterfaces@2024-05-01' = { - name: 'nic-learn-prod-${location}-${spokeName}-ubuntu' - location: location - properties: { - ipConfigurations: [ - { - name: 'default' - properties: { - subnet: { - id: vnet.properties.subnets[0].id - } - privateIPAllocationMethod: 'Dynamic' - } - } - ] - enableAcceleratedNetworking: true - } -} - -resource nic_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { - scope: nic - name: 'mic-to-hub-la' - properties: { - workspaceId: logAnalyticsWorkspaceId - metrics: [ - { - category: 'AllMetrics' - enabled: true - } - ] - } -} - -@description('A basic Ubuntu Linux virtual machine that will be attached to spoke.') -resource vm 'Microsoft.Compute/virtualMachines@2024-11-01' = { - name: 'vm-learn-prod-${location}-${spokeName}-ubuntu' - location: location - identity: { - // It is required by the Guest Configuration extension. - type: 'SystemAssigned' - } - properties: { - hardwareProfile: { - vmSize: 'Standard_DS1_v2' - } - storageProfile: { - osDisk: { - createOption: 'FromImage' - caching: 'ReadWrite' - managedDisk: { - storageAccountType: 'Standard_LRS' - } - deleteOption: 'Delete' - } - imageReference: { - publisher: 'canonical' - offer: '0001-com-ubuntu-server-jammy' - sku: '22_04-lts-gen2' - version: 'latest' - } - dataDisks: [] - } - diagnosticsProfile: { - bootDiagnostics: { - enabled: true - storageUri: null - } - } - networkProfile: { - networkInterfaces: [ - { - id: nic.id - properties: { - deleteOption: 'Delete' - primary: true - } - } - ] - } - osProfile: { - computerName: 'examplevm' - adminUsername: adminUsername - linuxConfiguration: { - disablePasswordAuthentication: true - ssh: { - publicKeys: [ - { - path: '/home/${adminUsername}/.ssh/authorized_keys' - keyData: sshKey - } - ] - } - patchSettings: { - //Machines should be configured to periodically check for missing system updates - assessmentMode: 'AutomaticByPlatform' - patchMode: 'AutomaticByPlatform' - } - provisionVMAgent: true - } - } - securityProfile: { - // We recommend enabling encryption at host for virtual machines and virtual machine scale sets to harden security. - encryptionAtHost: false - } - priority: 'Regular' - } -} - -// The Guest Configuration extension supports Azure governance at cloud scale, and can be installed after ensuring that a system-assigned identity is added at the VM level. This enable Azure policies to audit and report on configuration settings inside machines. -@description('Install the Guest Configuration extension for Azure auto-manage machine configuration on top regulatory, security, and operational compliance.') -resource vmGuestConfigExtension 'Microsoft.Compute/virtualMachines/extensions@2024-11-01' = { - parent: vm - name: 'Microsoft.GuestConfiguration' - location: location - properties: { - publisher: 'Microsoft.GuestConfiguration' - type: 'ConfigurationforLinux' // Use 'ConfigurationforWindows' if it's a Windows VM - typeHandlerVersion: '1.0' - autoUpgradeMinorVersion: true - enableAutomaticUpgrade: true - settings: {} - protectedSettings: {} - } -} - -output vnetId string = vnet.id diff --git a/solutions/avnm-secured-hub-and-spoke/images/hub-spoke-connected-group.png b/solutions/avnm-secured-hub-and-spoke/images/hub-spoke-connected-group.png deleted file mode 100644 index 5d466e9d..00000000 Binary files a/solutions/avnm-secured-hub-and-spoke/images/hub-spoke-connected-group.png and /dev/null differ