Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update/avnm secured hub and spoke #241

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 42 additions & 23 deletions solutions/avnm-secured-hub-and-spoke/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
page_type: sample
languages:
- azurepowershell
- azurecli
- azurepowershell
- azurecli
products:
- azure
- azure-virtual-network
Expand All @@ -15,44 +15,63 @@ urlFragment: virtual-network-manager-secured-hub-and-spoke

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: Create a Resource Group for the sample resources
### Step 1: Environment

```bash
LOCATION=eastus
RESOURCEGROUP_NAME=rg-hub-spoke-${LOCATION}

# Ensure the feature is enable
az feature register --namespace "Microsoft.Compute" --name "EncryptionAtHost"
```

### Step 2: Create a Resource Group for the sample resources

Create a resource group for the deployment.

```azurecli-interactive
az group create --name hub-spoke --location eastus
```bash
az group create --name ${RESOURCEGROUP_NAME} --location ${LOCATION}
```

### Step 2: Deploy infrastructure and Virtual Network Manager resources
### Step 3: Download bicep files

```azurecli-interactive
az deployment group create \
--resource-group hub-spoke \
--template-uri https://raw.githubusercontent.com/mspnp/samples/main/solutions/avnm-secured-hub-and-spoke/armTemplates/avnmResources.json
```bash
mkdir modules
cd modules
curl -o avnm.bicep https://raw.githubusercontent.com/mspnp/samples/main/solutions/avnm-secured-hub-and-spoke/bicep/modules/avnm.bicep
curl -o avnmDeploymentScript.bicep https://raw.githubusercontent.com/mspnp/samples/main/solutions/avnm-secured-hub-and-spoke/bicep/modules/avnmDeploymentScript.bicep
curl -o dynMemberPolicy.bicep https://raw.githubusercontent.com/mspnp/samples/main/solutions/avnm-secured-hub-and-spoke/bicep/modules/dynMemberPolicy.bicep
curl -o hub.bicep https://raw.githubusercontent.com/mspnp/samples/main/solutions/avnm-secured-hub-and-spoke/bicep/modules/hub.bicep
curl -o spoke.bicep https://raw.githubusercontent.com/mspnp/samples/main/solutions/avnm-secured-hub-and-spoke/bicep/modules/spoke.bicep
cd ..

curl -o main.bicep https://raw.githubusercontent.com/mspnp/samples/main/solutions/avnm-secured-hub-and-spoke/bicep/main.bicep
```

### Step 3: Deploy Virtual Network Manager Dynamic Network Group Policy resources
### Step 4: Deploy infrastructure and Virtual Network Manager resources

```bash
# Generate ssh key and get public data.
ssh-keygen -t rsa -b 2048

```azurecli-interactive
az deployment subscription create \
--template-uri https://raw.githubusercontent.com/mspnp/samples/main/solutions/avnm-secured-hub-and-spoke/armTemplates/avmnDynamicMembershipPolicy.json
az deployment sub create --template-file main.bicep -n avnm-secured-hub-and-spoke -l ${LOCATION} --parameters resourceGroupName=${RESOURCEGROUP_NAME} sshKey="$(cat ~/.ssh/id_rsa.pub)"
```

## Solution deployment parameters

| Parameter | Type | Description | Default |
|---|---|---|--|
| `location` | string | Deployment location | `resourceGroup().location` |
| `adminUserName` | string | The admin user name for deployed VMs. | `admin-avnm` |
| `adminPassword` | securestring | The admin password for deployed VMs. | `null` |
| Parameter | Type | Description | Default |
| --------------- | ------------ | ------------------------------------- | -------------------------- |
| `location` | string | Deployment location | `resourceGroup().location` |
| `adminUserName` | string | The admin user name for deployed VMs. | `admin-avnm` |
| `adminPassword` | securestring | The admin password for deployed VMs. | `null` |

## Step 5: Clean Up

## Bicep implementation

The links above use JSON Azure Resource Manager (ARM) templates to support network referencing. The ARM templates were generated from the following [source bicep file](https://github.com/mspnp/samples/blob/main/solutions/avnm-secured-hub-and-spoke/bicep/main.bicep), which has additional comments and considerations.
```bash
az group delete --name ${RESOURCEGROUP_NAME} --yes
```

## Microsoft Open Source Code of Conduct

Expand Down
16 changes: 5 additions & 11 deletions solutions/avnm-secured-hub-and-spoke/bicep/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ param resourceGroupName string

@description('The location of this regional hub. All resources, including spoke resources, will be deployed to this region.')
@minLength(6)
param location string
param location string = deployment().location

@description('Password for the test VMs deployed in the spokes')
@secure()
param adminPassword string
@description('your public key. Authentication to Linux machines should require SSH keys.')
param sshKey string

@description('Username for the test VMs deployed in the spokes; default: admin-avnm')
param adminUsername string = 'admin-avnm'
Expand Down Expand Up @@ -41,10 +40,9 @@ module spokeA 'modules/spoke.bicep' = {
name: 'spoke1-resources-deployment-${location}'
scope: resourceGroup
params: {
location: location
spokeName: '001'
spokeVnetPrefix: '10.1.0.0/16'
adminPassword: adminPassword
sshKey: sshKey
adminUsername: adminUsername
}
}
Expand All @@ -54,10 +52,9 @@ module spokeB 'modules/spoke.bicep' = {
name: 'spoke2-resources-deployment-${location}'
scope: resourceGroup
params: {
location: location
spokeName: '002'
spokeVnetPrefix: '10.2.0.0/16'
adminPassword: adminPassword
sshKey: sshKey
adminUsername: adminUsername
}
}
Expand All @@ -77,7 +74,6 @@ module avnm 'modules/avnm.bicep' = {
name: 'avnm'
scope: resourceGroup
params: {
location: location
hubVnetId: hub.outputs.hubVnetId
connectivityTopology: connectivityTopology
}
Expand All @@ -94,7 +90,6 @@ module deploymentScriptConnectivityConfigs 'modules/avnmDeploymentScript.bicep'
policy
]
params: {
location: location
userAssignedIdentityId: avnm.outputs.userAssignedIdentityId
configurationId: avnm.outputs.connectivityConfigurationId
configType: 'Connectivity'
Expand All @@ -110,7 +105,6 @@ module deploymentScriptSecurityConfigs 'modules/avnmDeploymentScript.bicep' = {
policy
]
params: {
location: location
userAssignedIdentityId: avnm.outputs.userAssignedIdentityId
configurationId: avnm.outputs.securtyAdminConfigurationId
configType: 'SecurityAdmin'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
param location string
param location string = resourceGroup().location
param hubVnetId string
param connectivityTopology string

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
param location string
param location string = resourceGroup().location
param userAssignedIdentityId string
param networkManagerName string
param configurationId string
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
param location string
param location string = resourceGroup().location

@description('The regional hub network.')
resource vnetHub 'Microsoft.Network/virtualNetworks@2022-09-01' = {
Expand Down
97 changes: 91 additions & 6 deletions solutions/avnm-secured-hub-and-spoke/bicep/modules/spoke.bicep
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
param location string
param spokeName string
param location string = resourceGroup().location
param spokeName string
param spokeVnetPrefix string
@secure()
param adminPassword string

param sshKey string
param adminUsername string = 'admin-avnm'

var protectionContainer = 'iaasvmcontainer;iaasvmcontainerv2;${resourceGroup().name};${vm.name}'
var protectedItem = 'vm;iaasvmcontainerv2;${resourceGroup().name};${vm.name}'

resource vnet 'Microsoft.Network/virtualNetworks@2022-01-01' = {
name: 'vnet-learn-prod-${location}-${toLower(spokeName)}'
location: location
Expand Down Expand Up @@ -41,6 +44,9 @@ resource nic 'Microsoft.Network/networkInterfaces@2022-01-01' = {
}
}
]
networkSecurityGroup: {
id: nsg.id
}
enableAcceleratedNetworking: true
}
}
Expand All @@ -49,6 +55,9 @@ resource nic 'Microsoft.Network/networkInterfaces@2022-01-01' = {
resource vm 'Microsoft.Compute/virtualMachines@2022-03-01' = {
name: 'vm-learn-prod-${location}-${spokeName}-ubuntu'
location: location
identity: {
type: 'SystemAssigned'
}
properties: {
hardwareProfile: {
vmSize: 'Standard_DS1_v2'
Expand Down Expand Up @@ -90,15 +99,91 @@ resource vm 'Microsoft.Compute/virtualMachines@2022-03-01' = {
osProfile: {
computerName: 'examplevm'
adminUsername: adminUsername
adminPassword: adminPassword
linuxConfiguration: {
disablePasswordAuthentication: false
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: {
//Virtual machines and virtual machine scale sets should have encryption at host enabled
encryptionAtHost: true
}
priority: 'Regular'
}
}

// Azure Backup should be enabled for virtual machines
resource recoveryServicesVault 'Microsoft.RecoveryServices/vaults@2021-08-01' = {
name: '${vm.name}-bkp'
location: location
sku: {
name: 'RS0'
tier: 'Standard'
}
properties: {}
}

resource vaultName_backupFabric_protectionContainer_protectedItem 'Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems@2020-02-02' = {
name: '${recoveryServicesVault.name}/Azure/${protectionContainer}/${protectedItem}'
properties: {
protectedItemType: 'Microsoft.Compute/virtualMachines'
policyId: '${recoveryServicesVault.id}/backupPolicies/DefaultPolicy'
sourceResourceId: vm.id
}
}

// Guest Configuration extension should be installed using the system-assigned managed identity
@description('Install the Guest Configuration extension for auditing purposes on the VM.')
resource guestConfigExtension 'Microsoft.Compute/virtualMachines/extensions@2021-03-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: {}
}
}

// Non-internet-facing virtual machines should be protected with network security groups
@description('The Network Security Group to protect the VM.')
resource nsg 'Microsoft.Network/networkSecurityGroups@2022-01-01' = {
name: 'nsg-learn-prod-${location}-${spokeName}-ubuntu'
location: location
properties: {
securityRules: [
{
name: 'DenyInternetAccess'
properties: {
priority: 2000
direction: 'Inbound'
access: 'Deny'
protocol: '*'
sourceAddressPrefix: 'Internet'
destinationAddressPrefix: '*'
destinationPortRange: '*'
sourcePortRange: '*'
}
}
]
}
}

output vnetId string = vnet.id