From 00995fd7c7670db4273f3e73f9616d495280414d Mon Sep 17 00:00:00 2001 From: Federico Arambarri Date: Mon, 24 Jun 2024 09:35:43 -0300 Subject: [PATCH 1/9] bicep --- .../README.md | 24 +- .../azuredeploy.bicep | 533 ++++++++++++++++++ 2 files changed, 552 insertions(+), 5 deletions(-) create mode 100644 solutions/azure-automation-state-configuration/azuredeploy.bicep diff --git a/solutions/azure-automation-state-configuration/README.md b/solutions/azure-automation-state-configuration/README.md index 7543834d..70ed4a9c 100644 --- a/solutions/azure-automation-state-configuration/README.md +++ b/solutions/azure-automation-state-configuration/README.md @@ -13,17 +13,25 @@ These Azure Resource Manager (ARM) template samples deploy an Azure Automation a ## Deploy sample +Set environment + +```bash + export LOCATION=eastus + export RESOURCEGROUP_BASE_NAME=rg-state-configuration + export RESOURCEGROUP=${RESOURCEGROUP_BASE_NAME}-${LOCATION} +``` + Create a resource group for the deployment. -```azurecli-interactive -az group create --name state-configuration --location eastus +```bash +az group create --name ${RESOURCEGROUP} --location ${LOCATION} ``` Run the following command to initiate the deployment. If you would like to adjust the number of virtual machines deployed, update the *windowsVMCount* and *linuxVMCount* values. -```azurecli -az deployment group create --resource-group state-configuration \ - --template-uri https://raw.githubusercontent.com/mspnp/samples/main/solutions/azure-automation-state-configuration/azuredeploy.json +```bash +curl ?? +az deployment group create --resource-group ${RESOURCEGROUP} -f ./azuredeploy.bicep ``` Once complete, click on the **Automation Account** resource and then **State configuration (DSC)** and notice that all virtual machines have been added to the system and are compliant. These machines have all had the PowerShell DSC configuration applied, which has installed a web server on each. @@ -51,6 +59,12 @@ Browse to the public IP address of any virtual machine to verify that a web serv | subnetName | string | Name for the subnet. | subnet | | location | string | Deployment location. | resourceGroup().location | +## Clean Up + +```bash +az group delete -n ${RESOURCEGROUP} -y +``` + ## Microsoft Open Source Code of Conduct This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). diff --git a/solutions/azure-automation-state-configuration/azuredeploy.bicep b/solutions/azure-automation-state-configuration/azuredeploy.bicep new file mode 100644 index 00000000..a6ab008c --- /dev/null +++ b/solutions/azure-automation-state-configuration/azuredeploy.bicep @@ -0,0 +1,533 @@ +param adminUserName string + +@secure() +param adminPassword string +param emailAddress string +param windowsVMCount int = 1 +param linuxVMCount int = 1 +param vmSize string = 'Standard_A1_v2' +param windowsConfiguration object = { + name: 'windowsfeatures' + description: 'A configuration for installing IIS.' + script: 'https://raw.githubusercontent.com/mspnp/samples/main/solutions/azure-automation-state-configuration/scripts/windows-config.ps1' +} +param linuxConfiguration object = { + name: 'linuxpackage' + description: 'A configuration for installing Nginx.' + script: 'https://raw.githubusercontent.com/mspnp/samples/main/solutions/azure-automation-state-configuration/scripts/linux-config.ps1' +} +param virtualNetworkName string = 'virtial-network' +param addressPrefix string = '10.0.0.0/16' +param subnetPrefix string = '10.0.0.0/24' +param subnetName string = 'subnet' +param location string = resourceGroup().location + +var logAnalyticsName = uniqueString(resourceGroup().id) +var automationAccountName = uniqueString(resourceGroup().id) +var moduleUri = 'https://devopsgallerystorage.blob.core.windows.net/packages/nx.1.0.0.nupkg' +var subnetRef = virtualNetworkName_subnet.id +var alertQuery = 'AzureDiagnostics\n| where Category == "DscNodeStatus"\n| where ResultType == "Failed"' +var windowsNicName = 'windows-nic-' +var windowsPIPName = 'windows-pip-' +var windowsVMName = 'windows-vm-' +var windowsOSVersion = '2016-Datacenter' +var linuxNicName = 'linux-nic-' +var linuxPIPName = 'linux-pip-' +var linuxVMNAme = 'linux-vm-' +var osVersion = '16.04.0-LTS' + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2020-10-01' = { + name: logAnalyticsName + location: location + properties: { + sku: { + name: 'PerGB2018' + } + features: { + searchVersion: 1 + } + } +} + +resource logAnalyticsName_91192b47_5f04_4215_a142_1fcb2b1622b1 'Microsoft.OperationalInsights/workspaces/savedSearches@2020-08-01' = { + parent: logAnalytics + name: '91192b47-5f04-4215-a142-1fcb2b1622b1' + properties: { + category: 'event' + displayName: 'Non Compliant DSC Node' + query: alertQuery + version: 2 + } +} + +resource non_compliant_dsc 'microsoft.insights/scheduledqueryrules@2018-04-16' = { + name: 'non-compliant-dsc' + location: location + properties: { + enabled: true + source: { + query: alertQuery + dataSourceId: logAnalytics.id + queryType: 'ResultCount' + } + schedule: { + frequencyInMinutes: 5 + timeWindowInMinutes: 5 + } + action: { + severity: '3' + aznsAction: { + actionGroup: [ + email_action.id + ] + } + trigger: { + thresholdOperator: 'GreaterThan' + threshold: 0 + } + 'odata.type': 'Microsoft.WindowsAzure.Management.Monitoring.Alerts.Models.Microsoft.AppInsights.Nexus.DataContracts.Resources.ScheduledQueryRules.AlertingAction' + } + } +} + +resource email_action 'microsoft.insights/actionGroups@2019-06-01' = { + name: 'email-action' + location: 'Global' + properties: { + groupShortName: 'emailService' + enabled: true + emailReceivers: [ + { + name: 'emailAction' + emailAddress: emailAddress + useCommonAlertSchema: false + } + ] + } +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2020-01-13-preview' = { + name: automationAccountName + location: location + properties: { + sku: { + name: 'Basic' + } + } +} + +resource automationAccountName_nx 'Microsoft.Automation/automationAccounts/modules@2020-01-13-preview' = { + parent: automationAccount + name: 'nx' + properties: { + contentLink: { + uri: moduleUri + } + } +} + +resource automationAccountName_linuxConfiguration_name 'Microsoft.Automation/automationAccounts/configurations@2019-06-01' = { + parent: automationAccount + name: '${linuxConfiguration.name}' + location: location + properties: { + logVerbose: false + description: linuxConfiguration.description + state: 'Published' + overwrite: 'true' + source: { + type: 'uri' + value: linuxConfiguration.script + } + } +} + +resource Microsoft_Automation_automationAccounts_compilationjobs_automationAccountName_linuxConfiguration_name 'Microsoft.Automation/automationAccounts/compilationjobs@2020-01-13-preview' = { + parent: automationAccount + name: '${linuxConfiguration.name}' + location: location + properties: { + configuration: { + name: linuxConfiguration.name + } + } + dependsOn: [ + automationAccountName_linuxConfiguration_name + automationAccountName_nx + ] +} + +resource automationAccountName_windowsConfiguration_name 'Microsoft.Automation/automationAccounts/configurations@2019-06-01' = { + parent: automationAccount + name: '${windowsConfiguration.name}' + location: location + properties: { + logVerbose: false + description: windowsConfiguration.description + state: 'Published' + overwrite: 'true' + source: { + type: 'uri' + value: windowsConfiguration.script + } + } +} + +resource Microsoft_Automation_automationAccounts_compilationjobs_automationAccountName_windowsConfiguration_name 'Microsoft.Automation/automationAccounts/compilationjobs@2020-01-13-preview' = { + parent: automationAccount + name: '${windowsConfiguration.name}' + location: location + properties: { + configuration: { + name: windowsConfiguration.name + } + } + dependsOn: [ + automationAccountName_windowsConfiguration_name + ] +} + +resource automationAccountName_Microsoft_Insights_default_logAnalytics 'Microsoft.Automation/automationAccounts/providers/diagnosticSettings@2017-05-01-preview' = { + name: '${automationAccountName}/Microsoft.Insights/default${logAnalyticsName}' + properties: { + workspaceId: logAnalytics.id + logs: [ + { + category: 'DscNodeStatus' + enabled: true + } + ] + } + dependsOn: [ + automationAccount + ] +} + +resource nsg 'Microsoft.Network/networkSecurityGroups@2020-08-01' = { + name: 'nsg' + location: location + properties: { + securityRules: [ + { + name: 'DenyAllInBound' + properties: { + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: '*' + destinationPortRange: '*' + destinationAddressPrefix: '*' + access: 'Deny' + priority: 1000 + direction: 'Inbound' + } + } + { + name: 'HTTP' + properties: { + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: '*' + destinationPortRange: '80' + destinationAddressPrefix: '*' + access: 'Allow' + priority: 100 + direction: 'Inbound' + } + } + ] + } +} + +resource nsg_Microsoft_Insights_default_logAnalytics 'Microsoft.Network/networkSecurityGroups/providers/diagnosticSettings@2017-05-01-preview' = { + name: 'nsg/Microsoft.Insights/default${logAnalyticsName}' + properties: { + workspaceId: logAnalytics.id + logs: [ + { + category: 'NetworkSecurityGroupEvent' + enabled: true + } + { + category: 'NetworkSecurityGroupRuleCounter' + enabled: true + } + ] + } + dependsOn: [ + nsg + ] +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2020-08-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + } + dependsOn: [ + nsg + ] +} + +resource virtualNetworkName_subnet 'Microsoft.Network/virtualNetworks/subnets@2020-08-01' = { + parent: virtualNetwork + name: subnetName + properties: { + addressPrefix: subnetPrefix + networkSecurityGroup: { + id: nsg.id + } + } +} + +resource windowsPIP 'Microsoft.Network/publicIPAddresses@2020-08-01' = [ + for i in range(0, windowsVMCount): { + name: '${windowsPIPName}${i}' + location: location + properties: { + publicIPAllocationMethod: 'Dynamic' + } + } +] + +resource windowsNic 'Microsoft.Network/networkInterfaces@2020-08-01' = [ + for i in range(0, windowsVMCount): { + name: '${windowsNicName}${i}' + location: location + properties: { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + privateIPAllocationMethod: 'Dynamic' + publicIPAddress: { + id: resourceId('Microsoft.Network/publicIPAddresses/', '${windowsPIPName}${i}') + } + subnet: { + id: subnetRef + } + } + } + ] + } + dependsOn: [ + windowsPIP + ] + } +] + +resource windowsVM 'Microsoft.Compute/virtualMachines@2020-12-01' = [ + for i in range(0, windowsVMCount): { + name: '${windowsVMName}${i}' + location: location + properties: { + hardwareProfile: { + vmSize: vmSize + } + osProfile: { + computerName: '${windowsVMName}${i}' + adminUsername: adminUserName + adminPassword: adminPassword + } + storageProfile: { + imageReference: { + publisher: 'MicrosoftWindowsServer' + offer: 'WindowsServer' + sku: windowsOSVersion + version: 'latest' + } + osDisk: { + createOption: 'FromImage' + } + } + networkProfile: { + networkInterfaces: [ + { + id: resourceId('Microsoft.Network/networkInterfaces', '${windowsNicName}${i}') + } + ] + } + } + dependsOn: [ + windowsNic + ] + } +] + +resource windowsVMName_Microsoft_Powershell_DSC 'Microsoft.Compute/virtualMachines/extensions@2020-12-01' = [ + for i in range(0, windowsVMCount): { + name: '${windowsVMName}${i}/Microsoft.Powershell.DSC' + location: location + properties: { + publisher: 'Microsoft.Powershell' + type: 'DSC' + typeHandlerVersion: '2.76' + autoUpgradeMinorVersion: true + protectedSettings: { + Items: { + registrationKeyPrivate: listKeys(automationAccount.id, '2019-06-01').Keys[0].value + } + } + settings: { + Properties: [ + { + Name: 'RegistrationKey' + Value: { + UserName: 'PLACEHOLDER_DONOTUSE' + Password: 'PrivateSettingsRef:registrationKeyPrivate' + } + TypeName: 'System.Management.Automation.PSCredential' + } + { + Name: 'RegistrationUrl' + Value: automationAccount.properties.registrationUrl + TypeName: 'System.String' + } + { + Name: 'NodeConfigurationName' + Value: '${windowsConfiguration.name}.localhost' + TypeName: 'System.String' + } + { + Name: 'ConfigurationMode' + Value: 'ApplyAndMonitor' + TypeName: 'System.String' + } + { + Name: 'ConfigurationModeFrequencyMins' + Value: 15 + TypeName: 'System.Int32' + } + { + Name: 'RefreshFrequencyMins' + Value: 30 + TypeName: 'System.Int32' + } + { + Name: 'RebootNodeIfNeeded' + Value: true + TypeName: 'System.Boolean' + } + { + Name: 'ActionAfterReboot' + Value: 'ContinueConfiguration' + TypeName: 'System.String' + } + { + Name: 'AllowModuleOverwrite' + Value: false + TypeName: 'System.Boolean' + } + ] + } + } + dependsOn: [ + windowsVM + ] + } +] + +resource linuxPIP 'Microsoft.Network/publicIPAddresses@2020-08-01' = [ + for i in range(0, linuxVMCount): { + name: '${linuxPIPName}${i}' + location: location + properties: { + publicIPAllocationMethod: 'Dynamic' + } + } +] + +resource linuxNic 'Microsoft.Network/networkInterfaces@2020-08-01' = [ + for i in range(0, linuxVMCount): { + name: concat(linuxNicName, i) + location: location + properties: { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + privateIPAllocationMethod: 'Dynamic' + publicIPAddress: { + id: resourceId('Microsoft.Network/publicIPAddresses/', concat(linuxPIPName, i)) + } + subnet: { + id: subnetRef + } + } + } + ] + } + dependsOn: [ + linuxPIP + ] + } +] + +resource linuxVMN 'Microsoft.Compute/virtualMachines@2020-12-01' = [ + for i in range(0, linuxVMCount): { + name: '${linuxVMNAme}${i}' + location: location + properties: { + hardwareProfile: { + vmSize: vmSize + } + osProfile: { + computerName: '${linuxVMNAme}${i}' + adminUsername: adminUserName + adminPassword: adminPassword + } + storageProfile: { + imageReference: { + publisher: 'Canonical' + offer: 'UbuntuServer' + sku: osVersion + version: 'latest' + } + osDisk: { + createOption: 'FromImage' + } + } + networkProfile: { + networkInterfaces: [ + { + id: resourceId('Microsoft.Network/networkInterfaces', '${linuxNicName}${i}') + } + ] + } + } + dependsOn: [ + linuxNic + ] + } +] + +resource linuxVMNAme_enabledsc 'Microsoft.Compute/virtualMachines/extensions@2020-12-01' = [ + for i in range(0, linuxVMCount): { + name: '${linuxVMNAme}${i}/enabledsc' + location: location + properties: { + publisher: 'Microsoft.OSTCExtensions' + type: 'DSCForLinux' + typeHandlerVersion: '2.7' + autoUpgradeMinorVersion: true + settings: { + ExtensionAction: 'Register' + NodeConfigurationName: '${linuxConfiguration.name}.localhost' + RefreshFrequencyMins: 30 + ConfigurationMode: 'applyAndAutoCorrect' + ConfigurationModeFrequencyMins: 15 + RegistrationUrl: automationAccount.properties.registrationUrl + } + protectedSettings: { + RegistrationKey: listKeys(automationAccount.id, '2019-06-01').Keys[0].value + } + } + dependsOn: [ + linuxVMN + ] + } +] From ba7ec1d7564bb4b2b6f40c8c454c651a8c1adb5b Mon Sep 17 00:00:00 2001 From: Federico Arambarri Date: Mon, 24 Jun 2024 16:01:16 -0300 Subject: [PATCH 2/9] update --- .../azuredeploy.bicep | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/solutions/azure-automation-state-configuration/azuredeploy.bicep b/solutions/azure-automation-state-configuration/azuredeploy.bicep index a6ab008c..e066e533 100644 --- a/solutions/azure-automation-state-configuration/azuredeploy.bicep +++ b/solutions/azure-automation-state-configuration/azuredeploy.bicep @@ -1,3 +1,5 @@ +param location string = resourceGroup().location + param adminUserName string @secure() @@ -20,7 +22,6 @@ param virtualNetworkName string = 'virtial-network' param addressPrefix string = '10.0.0.0/16' param subnetPrefix string = '10.0.0.0/24' param subnetName string = 'subnet' -param location string = resourceGroup().location var logAnalyticsName = uniqueString(resourceGroup().id) var automationAccountName = uniqueString(resourceGroup().id) @@ -36,7 +37,7 @@ var linuxPIPName = 'linux-pip-' var linuxVMNAme = 'linux-vm-' var osVersion = '16.04.0-LTS' -resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2020-10-01' = { +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { name: logAnalyticsName location: location properties: { @@ -64,7 +65,7 @@ resource non_compliant_dsc 'microsoft.insights/scheduledqueryrules@2018-04-16' = name: 'non-compliant-dsc' location: location properties: { - enabled: true + enabled: 'true' source: { query: alertQuery dataSourceId: logAnalytics.id @@ -90,7 +91,7 @@ resource non_compliant_dsc 'microsoft.insights/scheduledqueryrules@2018-04-16' = } } -resource email_action 'microsoft.insights/actionGroups@2019-06-01' = { +resource email_action 'microsoft.insights/actionGroups@2023-01-01' = { name: 'email-action' location: 'Global' properties: { @@ -106,7 +107,7 @@ resource email_action 'microsoft.insights/actionGroups@2019-06-01' = { } } -resource automationAccount 'Microsoft.Automation/automationAccounts@2020-01-13-preview' = { +resource automationAccount 'Microsoft.Automation/automationAccounts@2023-11-01' = { name: automationAccountName location: location properties: { @@ -116,7 +117,7 @@ resource automationAccount 'Microsoft.Automation/automationAccounts@2020-01-13-p } } -resource automationAccountName_nx 'Microsoft.Automation/automationAccounts/modules@2020-01-13-preview' = { +resource automationAccountName_nx 'Microsoft.Automation/automationAccounts/modules@2023-11-01' = { parent: automationAccount name: 'nx' properties: { @@ -126,7 +127,7 @@ resource automationAccountName_nx 'Microsoft.Automation/automationAccounts/modul } } -resource automationAccountName_linuxConfiguration_name 'Microsoft.Automation/automationAccounts/configurations@2019-06-01' = { +resource automationAccountName_linuxConfiguration_name 'Microsoft.Automation/automationAccounts/configurations@2023-11-01' = { parent: automationAccount name: '${linuxConfiguration.name}' location: location @@ -142,7 +143,7 @@ resource automationAccountName_linuxConfiguration_name 'Microsoft.Automation/aut } } -resource Microsoft_Automation_automationAccounts_compilationjobs_automationAccountName_linuxConfiguration_name 'Microsoft.Automation/automationAccounts/compilationjobs@2020-01-13-preview' = { +resource Microsoft_Automation_automationAccounts_compilationjobs_automationAccountName_linuxConfiguration_name 'Microsoft.Automation/automationAccounts/compilationjobs@2023-05-15-preview' = { parent: automationAccount name: '${linuxConfiguration.name}' location: location @@ -157,7 +158,7 @@ resource Microsoft_Automation_automationAccounts_compilationjobs_automationAccou ] } -resource automationAccountName_windowsConfiguration_name 'Microsoft.Automation/automationAccounts/configurations@2019-06-01' = { +resource automationAccountName_windowsConfiguration_name 'Microsoft.Automation/automationAccounts/configurations@2023-05-15-preview' = { parent: automationAccount name: '${windowsConfiguration.name}' location: location @@ -173,7 +174,7 @@ resource automationAccountName_windowsConfiguration_name 'Microsoft.Automation/a } } -resource Microsoft_Automation_automationAccounts_compilationjobs_automationAccountName_windowsConfiguration_name 'Microsoft.Automation/automationAccounts/compilationjobs@2020-01-13-preview' = { +resource Microsoft_Automation_automationAccounts_compilationjobs_automationAccountName_windowsConfiguration_name 'Microsoft.Automation/automationAccounts/compilationjobs@2023-05-15-preview' = { parent: automationAccount name: '${windowsConfiguration.name}' location: location @@ -187,7 +188,7 @@ resource Microsoft_Automation_automationAccounts_compilationjobs_automationAccou ] } -resource automationAccountName_Microsoft_Insights_default_logAnalytics 'Microsoft.Automation/automationAccounts/providers/diagnosticSettings@2017-05-01-preview' = { +resource automationAccountName_Microsoft_Insights_default_logAnalytics 'Microsoft.Automation/automationAccounts/providers/diagnosticSettings@2021-05-01-preview' = { name: '${automationAccountName}/Microsoft.Insights/default${logAnalyticsName}' properties: { workspaceId: logAnalytics.id @@ -203,7 +204,7 @@ resource automationAccountName_Microsoft_Insights_default_logAnalytics 'Microsof ] } -resource nsg 'Microsoft.Network/networkSecurityGroups@2020-08-01' = { +resource nsg 'Microsoft.Network/networkSecurityGroups@2023-11-01' = { name: 'nsg' location: location properties: { @@ -238,7 +239,7 @@ resource nsg 'Microsoft.Network/networkSecurityGroups@2020-08-01' = { } } -resource nsg_Microsoft_Insights_default_logAnalytics 'Microsoft.Network/networkSecurityGroups/providers/diagnosticSettings@2017-05-01-preview' = { +resource nsg_Microsoft_Insights_default_logAnalytics 'Microsoft.Network/networkSecurityGroups/providers/diagnosticSettings@2021-05-01-preview' = { name: 'nsg/Microsoft.Insights/default${logAnalyticsName}' properties: { workspaceId: logAnalytics.id @@ -258,7 +259,7 @@ resource nsg_Microsoft_Insights_default_logAnalytics 'Microsoft.Network/networkS ] } -resource virtualNetwork 'Microsoft.Network/virtualNetworks@2020-08-01' = { +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-11-01' = { name: virtualNetworkName location: location properties: { @@ -273,7 +274,7 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2020-08-01' = { ] } -resource virtualNetworkName_subnet 'Microsoft.Network/virtualNetworks/subnets@2020-08-01' = { +resource virtualNetworkName_subnet 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' = { parent: virtualNetwork name: subnetName properties: { @@ -284,7 +285,7 @@ resource virtualNetworkName_subnet 'Microsoft.Network/virtualNetworks/subnets@20 } } -resource windowsPIP 'Microsoft.Network/publicIPAddresses@2020-08-01' = [ +resource windowsPIP 'Microsoft.Network/publicIPAddresses@2023-11-01' = [ for i in range(0, windowsVMCount): { name: '${windowsPIPName}${i}' location: location @@ -294,7 +295,7 @@ resource windowsPIP 'Microsoft.Network/publicIPAddresses@2020-08-01' = [ } ] -resource windowsNic 'Microsoft.Network/networkInterfaces@2020-08-01' = [ +resource windowsNic 'Microsoft.Network/networkInterfaces@2023-11-01' = [ for i in range(0, windowsVMCount): { name: '${windowsNicName}${i}' location: location @@ -320,7 +321,7 @@ resource windowsNic 'Microsoft.Network/networkInterfaces@2020-08-01' = [ } ] -resource windowsVM 'Microsoft.Compute/virtualMachines@2020-12-01' = [ +resource windowsVM 'Microsoft.Compute/virtualMachines@2023-09-01' = [ for i in range(0, windowsVMCount): { name: '${windowsVMName}${i}' location: location @@ -358,7 +359,7 @@ resource windowsVM 'Microsoft.Compute/virtualMachines@2020-12-01' = [ } ] -resource windowsVMName_Microsoft_Powershell_DSC 'Microsoft.Compute/virtualMachines/extensions@2020-12-01' = [ +resource windowsVMName_Microsoft_Powershell_DSC 'Microsoft.Compute/virtualMachines/extensions@2023-09-01' = [ for i in range(0, windowsVMCount): { name: '${windowsVMName}${i}/Microsoft.Powershell.DSC' location: location @@ -384,6 +385,7 @@ resource windowsVMName_Microsoft_Powershell_DSC 'Microsoft.Compute/virtualMachin } { Name: 'RegistrationUrl' +#disable-next-line BCP053 Value: automationAccount.properties.registrationUrl TypeName: 'System.String' } @@ -431,7 +433,7 @@ resource windowsVMName_Microsoft_Powershell_DSC 'Microsoft.Compute/virtualMachin } ] -resource linuxPIP 'Microsoft.Network/publicIPAddresses@2020-08-01' = [ +resource linuxPIP 'Microsoft.Network/publicIPAddresses@2023-09-01' = [ for i in range(0, linuxVMCount): { name: '${linuxPIPName}${i}' location: location @@ -441,9 +443,9 @@ resource linuxPIP 'Microsoft.Network/publicIPAddresses@2020-08-01' = [ } ] -resource linuxNic 'Microsoft.Network/networkInterfaces@2020-08-01' = [ +resource linuxNic 'Microsoft.Network/networkInterfaces@2023-09-01' = [ for i in range(0, linuxVMCount): { - name: concat(linuxNicName, i) + name: '${linuxNicName}${i}' location: location properties: { ipConfigurations: [ @@ -452,7 +454,7 @@ resource linuxNic 'Microsoft.Network/networkInterfaces@2020-08-01' = [ properties: { privateIPAllocationMethod: 'Dynamic' publicIPAddress: { - id: resourceId('Microsoft.Network/publicIPAddresses/', concat(linuxPIPName, i)) + id: resourceId('Microsoft.Network/publicIPAddresses/', '${linuxPIPName}${i}') } subnet: { id: subnetRef @@ -467,7 +469,7 @@ resource linuxNic 'Microsoft.Network/networkInterfaces@2020-08-01' = [ } ] -resource linuxVMN 'Microsoft.Compute/virtualMachines@2020-12-01' = [ +resource linuxVMN 'Microsoft.Compute/virtualMachines@2023-09-01' = [ for i in range(0, linuxVMCount): { name: '${linuxVMNAme}${i}' location: location @@ -505,7 +507,7 @@ resource linuxVMN 'Microsoft.Compute/virtualMachines@2020-12-01' = [ } ] -resource linuxVMNAme_enabledsc 'Microsoft.Compute/virtualMachines/extensions@2020-12-01' = [ +resource linuxVMNAme_enabledsc 'Microsoft.Compute/virtualMachines/extensions@2023-09-01' = [ for i in range(0, linuxVMCount): { name: '${linuxVMNAme}${i}/enabledsc' location: location From 3d7aa01d07b427a80a422aecb76cffef1b70f330 Mon Sep 17 00:00:00 2001 From: Federico Arambarri Date: Tue, 25 Jun 2024 07:34:51 -0300 Subject: [PATCH 3/9] curl --- solutions/azure-automation-state-configuration/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/azure-automation-state-configuration/README.md b/solutions/azure-automation-state-configuration/README.md index 70ed4a9c..b2687d9c 100644 --- a/solutions/azure-automation-state-configuration/README.md +++ b/solutions/azure-automation-state-configuration/README.md @@ -30,7 +30,7 @@ az group create --name ${RESOURCEGROUP} --location ${LOCATION} Run the following command to initiate the deployment. If you would like to adjust the number of virtual machines deployed, update the *windowsVMCount* and *linuxVMCount* values. ```bash -curl ?? +curl -o azuredeploy.bicep https://raw.githubusercontent.com/mspnp/samples/main/solutions/azure-automation-state-configuration/azuredeploy.bicep az deployment group create --resource-group ${RESOURCEGROUP} -f ./azuredeploy.bicep ``` From 6915c0649564c343159a6114cff18a60e42a2c54 Mon Sep 17 00:00:00 2001 From: Federico Arambarri Date: Thu, 12 Sep 2024 13:28:07 -0300 Subject: [PATCH 4/9] Security Advices --- .../azuredeploy.bicep | 57 +++++++++++++++++-- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/solutions/azure-automation-state-configuration/azuredeploy.bicep b/solutions/azure-automation-state-configuration/azuredeploy.bicep index e066e533..e69b9290 100644 --- a/solutions/azure-automation-state-configuration/azuredeploy.bicep +++ b/solutions/azure-automation-state-configuration/azuredeploy.bicep @@ -7,7 +7,7 @@ param adminPassword string param emailAddress string param windowsVMCount int = 1 param linuxVMCount int = 1 -param vmSize string = 'Standard_A1_v2' +param vmSize string = 'Standard_DS1_v2' param windowsConfiguration object = { name: 'windowsfeatures' description: 'A configuration for installing IIS.' @@ -134,8 +134,6 @@ resource automationAccountName_linuxConfiguration_name 'Microsoft.Automation/aut properties: { logVerbose: false description: linuxConfiguration.description - state: 'Published' - overwrite: 'true' source: { type: 'uri' value: linuxConfiguration.script @@ -165,8 +163,6 @@ resource automationAccountName_windowsConfiguration_name 'Microsoft.Automation/a properties: { logVerbose: false description: windowsConfiguration.description - state: 'Published' - overwrite: 'true' source: { type: 'uri' value: windowsConfiguration.script @@ -325,6 +321,9 @@ resource windowsVM 'Microsoft.Compute/virtualMachines@2023-09-01' = [ for i in range(0, windowsVMCount): { name: '${windowsVMName}${i}' location: location + identity: { + type: 'SystemAssigned' + } properties: { hardwareProfile: { vmSize: vmSize @@ -352,6 +351,10 @@ resource windowsVM 'Microsoft.Compute/virtualMachines@2023-09-01' = [ } ] } + securityProfile: { + //Virtual machines and virtual machine scale sets should have encryption at host enabled + encryptionAtHost: true + } } dependsOn: [ windowsNic @@ -359,6 +362,24 @@ resource windowsVM 'Microsoft.Compute/virtualMachines@2023-09-01' = [ } ] + +resource guestConfigExtensionWindows 'Microsoft.Compute/virtualMachines/extensions@2021-03-01' = [ + for i in range(0, windowsVMCount): { + parent: windowsVM[i] + name: 'Microsoft.GuestConfiguration${windowsVM[i].name}' + location: location + properties: { + publisher: 'Microsoft.GuestConfiguration' + type: 'ConfigurationforWindows' + typeHandlerVersion: '1.0' + autoUpgradeMinorVersion: true + enableAutomaticUpgrade: true + settings: {} + protectedSettings: {} + } + } +] + resource windowsVMName_Microsoft_Powershell_DSC 'Microsoft.Compute/virtualMachines/extensions@2023-09-01' = [ for i in range(0, windowsVMCount): { name: '${windowsVMName}${i}/Microsoft.Powershell.DSC' @@ -385,7 +406,7 @@ resource windowsVMName_Microsoft_Powershell_DSC 'Microsoft.Compute/virtualMachin } { Name: 'RegistrationUrl' -#disable-next-line BCP053 + #disable-next-line BCP053 Value: automationAccount.properties.registrationUrl TypeName: 'System.String' } @@ -473,6 +494,9 @@ resource linuxVMN 'Microsoft.Compute/virtualMachines@2023-09-01' = [ for i in range(0, linuxVMCount): { name: '${linuxVMNAme}${i}' location: location + identity: { + type: 'SystemAssigned' + } properties: { hardwareProfile: { vmSize: vmSize @@ -500,6 +524,10 @@ resource linuxVMN 'Microsoft.Compute/virtualMachines@2023-09-01' = [ } ] } + securityProfile: { + //Virtual machines and virtual machine scale sets should have encryption at host enabled + encryptionAtHost: true + } } dependsOn: [ linuxNic @@ -507,6 +535,23 @@ resource linuxVMN 'Microsoft.Compute/virtualMachines@2023-09-01' = [ } ] +resource guestConfigExtensionLinux 'Microsoft.Compute/virtualMachines/extensions@2021-03-01' = [ + for i in range(0, linuxVMCount): { + parent: linuxVMN[i] + name: 'Microsoft.GuestConfiguration${linuxVMN[i].name}' + location: location + properties: { + publisher: 'Microsoft.GuestConfiguration' + type: 'ConfigurationforLinux' + typeHandlerVersion: '1.0' + autoUpgradeMinorVersion: true + enableAutomaticUpgrade: true + settings: {} + protectedSettings: {} + } + } +] + resource linuxVMNAme_enabledsc 'Microsoft.Compute/virtualMachines/extensions@2023-09-01' = [ for i in range(0, linuxVMCount): { name: '${linuxVMNAme}${i}/enabledsc' From 3b3ae64168a292abd85727e03f24a016a9461981 Mon Sep 17 00:00:00 2001 From: Federico Arambarri Date: Thu, 12 Sep 2024 15:51:28 -0300 Subject: [PATCH 5/9] Authentication to Linux machines should require SSH keys --- .../README.md | 8 +++++++- .../azuredeploy.bicep | 18 +++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/solutions/azure-automation-state-configuration/README.md b/solutions/azure-automation-state-configuration/README.md index b2687d9c..86b785ad 100644 --- a/solutions/azure-automation-state-configuration/README.md +++ b/solutions/azure-automation-state-configuration/README.md @@ -31,7 +31,13 @@ Run the following command to initiate the deployment. If you would like to adjus ```bash curl -o azuredeploy.bicep https://raw.githubusercontent.com/mspnp/samples/main/solutions/azure-automation-state-configuration/azuredeploy.bicep -az deployment group create --resource-group ${RESOURCEGROUP} -f ./azuredeploy.bicep + +# Generate ssh key and get public data. +ssh-keygen -t rsa -b 2048 + +export SSH_KEY=$(cat ~/.ssh/id_rsa.pub) + +az deployment group create --resource-group ${RESOURCEGROUP} -f ./azuredeploy.bicep --parameters sshKey="${SSH_KEY}" ``` Once complete, click on the **Automation Account** resource and then **State configuration (DSC)** and notice that all virtual machines have been added to the system and are compliant. These machines have all had the PowerShell DSC configuration applied, which has installed a web server on each. diff --git a/solutions/azure-automation-state-configuration/azuredeploy.bicep b/solutions/azure-automation-state-configuration/azuredeploy.bicep index e69b9290..8388b775 100644 --- a/solutions/azure-automation-state-configuration/azuredeploy.bicep +++ b/solutions/azure-automation-state-configuration/azuredeploy.bicep @@ -2,6 +2,8 @@ param location string = resourceGroup().location param adminUserName string +@description('your public key. Authentication to Linux machines should require SSH keys.') +param sshKey string @secure() param adminPassword string param emailAddress string @@ -362,7 +364,6 @@ resource windowsVM 'Microsoft.Compute/virtualMachines@2023-09-01' = [ } ] - resource guestConfigExtensionWindows 'Microsoft.Compute/virtualMachines/extensions@2021-03-01' = [ for i in range(0, windowsVMCount): { parent: windowsVM[i] @@ -370,7 +371,7 @@ resource guestConfigExtensionWindows 'Microsoft.Compute/virtualMachines/extensio location: location properties: { publisher: 'Microsoft.GuestConfiguration' - type: 'ConfigurationforWindows' + type: 'ConfigurationforWindows' typeHandlerVersion: '1.0' autoUpgradeMinorVersion: true enableAutomaticUpgrade: true @@ -504,7 +505,18 @@ resource linuxVMN 'Microsoft.Compute/virtualMachines@2023-09-01' = [ osProfile: { computerName: '${linuxVMNAme}${i}' adminUsername: adminUserName - adminPassword: adminPassword + linuxConfiguration: { + disablePasswordAuthentication: true + ssh: { + publicKeys: [ + { + path: '/home/${adminUserName}/.ssh/authorized_keys' + keyData: sshKey + } + ] + } + provisionVMAgent: true + } } storageProfile: { imageReference: { From ec639bc203bce76dd57b7f4280dcaaa9271c7914 Mon Sep 17 00:00:00 2001 From: Federico Arambarri Date: Fri, 13 Sep 2024 11:15:15 -0300 Subject: [PATCH 6/9] Machines should be configured to periodically check for missing system updates --- .../azuredeploy.bicep | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/solutions/azure-automation-state-configuration/azuredeploy.bicep b/solutions/azure-automation-state-configuration/azuredeploy.bicep index 8388b775..c8063814 100644 --- a/solutions/azure-automation-state-configuration/azuredeploy.bicep +++ b/solutions/azure-automation-state-configuration/azuredeploy.bicep @@ -334,6 +334,14 @@ resource windowsVM 'Microsoft.Compute/virtualMachines@2023-09-01' = [ computerName: '${windowsVMName}${i}' adminUsername: adminUserName adminPassword: adminPassword + windowsConfiguration: { + enableAutomaticUpdates: true + patchSettings: { + //Machines should be configured to periodically check for missing system updates + assessmentMode: 'AutomaticByPlatform' + patchMode: 'AutomaticByPlatform' + } + } } storageProfile: { imageReference: { @@ -506,6 +514,11 @@ resource linuxVMN 'Microsoft.Compute/virtualMachines@2023-09-01' = [ computerName: '${linuxVMNAme}${i}' adminUsername: adminUserName linuxConfiguration: { + patchSettings: { + //Machines should be configured to periodically check for missing system updates + assessmentMode: 'AutomaticByPlatform' + patchMode: 'AutomaticByPlatform ' + } disablePasswordAuthentication: true ssh: { publicKeys: [ From ed3cd283a65690302a2875cb36f85885f2fb04f5 Mon Sep 17 00:00:00 2001 From: Federico Arambarri Date: Fri, 13 Sep 2024 11:44:47 -0300 Subject: [PATCH 7/9] Addressing comment --- solutions/azure-automation-state-configuration/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/solutions/azure-automation-state-configuration/README.md b/solutions/azure-automation-state-configuration/README.md index 86b785ad..b3018ee6 100644 --- a/solutions/azure-automation-state-configuration/README.md +++ b/solutions/azure-automation-state-configuration/README.md @@ -35,9 +35,7 @@ curl -o azuredeploy.bicep https://raw.githubusercontent.com/mspnp/samples/main/s # Generate ssh key and get public data. ssh-keygen -t rsa -b 2048 -export SSH_KEY=$(cat ~/.ssh/id_rsa.pub) - -az deployment group create --resource-group ${RESOURCEGROUP} -f ./azuredeploy.bicep --parameters sshKey="${SSH_KEY}" +az deployment group create --resource-group ${RESOURCEGROUP} -f ./azuredeploy.bicep --parameters sshKey="$(cat ~/.ssh/id_rsa.pub)" ``` Once complete, click on the **Automation Account** resource and then **State configuration (DSC)** and notice that all virtual machines have been added to the system and are compliant. These machines have all had the PowerShell DSC configuration applied, which has installed a web server on each. From f9a0a6f2060f6f8bd433cfa1f57a20d44a2eda89 Mon Sep 17 00:00:00 2001 From: Federico Arambarri Date: Fri, 13 Sep 2024 14:26:17 -0300 Subject: [PATCH 8/9] Azure Backup should be enabled for virtual machines --- .../azuredeploy.bicep | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/solutions/azure-automation-state-configuration/azuredeploy.bicep b/solutions/azure-automation-state-configuration/azuredeploy.bicep index c8063814..93d62599 100644 --- a/solutions/azure-automation-state-configuration/azuredeploy.bicep +++ b/solutions/azure-automation-state-configuration/azuredeploy.bicep @@ -337,7 +337,7 @@ resource windowsVM 'Microsoft.Compute/virtualMachines@2023-09-01' = [ windowsConfiguration: { enableAutomaticUpdates: true patchSettings: { - //Machines should be configured to periodically check for missing system updates + //Machines should be configured to periodically check for missing system updates assessmentMode: 'AutomaticByPlatform' patchMode: 'AutomaticByPlatform' } @@ -389,6 +389,17 @@ resource guestConfigExtensionWindows 'Microsoft.Compute/virtualMachines/extensio } ] +resource vaultName_backupFabric_protectionContainer_protectedItem_WindownsVM 'Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems@2020-02-02' = [ + for i in range(0, windowsVMCount): { + name: '${recoveryServicesVault.name}/Azure/iaasvmcontainer;iaasvmcontainerv2;${resourceGroup().name};${windowsVM[i].name}/vm;iaasvmcontainerv2;${resourceGroup().name};${windowsVM[i].name}' + properties: { + protectedItemType: 'Microsoft.Compute/virtualMachines' + policyId: '${recoveryServicesVault.id}/backupPolicies/DefaultPolicy' + sourceResourceId: windowsVM[i].id + } + } +] + resource windowsVMName_Microsoft_Powershell_DSC 'Microsoft.Compute/virtualMachines/extensions@2023-09-01' = [ for i in range(0, windowsVMCount): { name: '${windowsVMName}${i}/Microsoft.Powershell.DSC' @@ -515,7 +526,7 @@ resource linuxVMN 'Microsoft.Compute/virtualMachines@2023-09-01' = [ adminUsername: adminUserName linuxConfiguration: { patchSettings: { - //Machines should be configured to periodically check for missing system updates + //Machines should be configured to periodically check for missing system updates assessmentMode: 'AutomaticByPlatform' patchMode: 'AutomaticByPlatform ' } @@ -577,6 +588,17 @@ resource guestConfigExtensionLinux 'Microsoft.Compute/virtualMachines/extensions } ] +resource vaultName_backupFabric_protectionContainer_protectedItem_LinuxVM 'Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems@2020-02-02' = [ + for i in range(0, linuxVMCount): { + name: '${recoveryServicesVault.name}/Azure/iaasvmcontainer;iaasvmcontainerv2;${resourceGroup().name};${linuxVMN[i].name}/vm;iaasvmcontainerv2;${resourceGroup().name};${linuxVMN[i].name}' + properties: { + protectedItemType: 'Microsoft.Compute/virtualMachines' + policyId: '${recoveryServicesVault.id}/backupPolicies/DefaultPolicy' + sourceResourceId: linuxVMN[i].id + } + } +] + resource linuxVMNAme_enabledsc 'Microsoft.Compute/virtualMachines/extensions@2023-09-01' = [ for i in range(0, linuxVMCount): { name: '${linuxVMNAme}${i}/enabledsc' @@ -603,3 +625,14 @@ resource linuxVMNAme_enabledsc 'Microsoft.Compute/virtualMachines/extensions@202 ] } ] + +// Azure Backup should be enabled for virtual machines +resource recoveryServicesVault 'Microsoft.RecoveryServices/vaults@2021-08-01' = { + name: 'vm-bkp' + location: location + sku: { + name: 'RS0' + tier: 'Standard' + } + properties: {} +} From f50cfc00479ef1e99e55d6ba3a104eef72956e09 Mon Sep 17 00:00:00 2001 From: Federico Arambarri Date: Mon, 16 Sep 2024 08:53:55 -0300 Subject: [PATCH 9/9] Adjustments --- .../azuredeploy.bicep | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/solutions/azure-automation-state-configuration/azuredeploy.bicep b/solutions/azure-automation-state-configuration/azuredeploy.bicep index 93d62599..ccd43722 100644 --- a/solutions/azure-automation-state-configuration/azuredeploy.bicep +++ b/solutions/azure-automation-state-configuration/azuredeploy.bicep @@ -357,7 +357,7 @@ resource windowsVM 'Microsoft.Compute/virtualMachines@2023-09-01' = [ networkProfile: { networkInterfaces: [ { - id: resourceId('Microsoft.Network/networkInterfaces', '${windowsNicName}${i}') + id: resourceId('Microsoft.Network/networkInterfaces', windowsNic[i].name) } ] } @@ -366,16 +366,14 @@ resource windowsVM 'Microsoft.Compute/virtualMachines@2023-09-01' = [ encryptionAtHost: true } } - dependsOn: [ - windowsNic - ] } ] +// https://learn.microsoft.com/azure/virtual-machines/extensions/guest-configuration#bicep-template resource guestConfigExtensionWindows 'Microsoft.Compute/virtualMachines/extensions@2021-03-01' = [ for i in range(0, windowsVMCount): { parent: windowsVM[i] - name: 'Microsoft.GuestConfiguration${windowsVM[i].name}' + name: 'AzurePolicyforWindows${windowsVM[i].name}' location: location properties: { publisher: 'Microsoft.GuestConfiguration' @@ -556,7 +554,7 @@ resource linuxVMN 'Microsoft.Compute/virtualMachines@2023-09-01' = [ networkProfile: { networkInterfaces: [ { - id: resourceId('Microsoft.Network/networkInterfaces', '${linuxNicName}${i}') + id: resourceId('Microsoft.Network/networkInterfaces', linuxNic[i].name) } ] } @@ -565,20 +563,18 @@ resource linuxVMN 'Microsoft.Compute/virtualMachines@2023-09-01' = [ encryptionAtHost: true } } - dependsOn: [ - linuxNic - ] } ] +// https://learn.microsoft.com/azure/virtual-machines/extensions/guest-configuration#bicep-template resource guestConfigExtensionLinux 'Microsoft.Compute/virtualMachines/extensions@2021-03-01' = [ for i in range(0, linuxVMCount): { parent: linuxVMN[i] - name: 'Microsoft.GuestConfiguration${linuxVMN[i].name}' + name: 'Microsoft.AzurePolicyforLinux${linuxVMN[i].name}' location: location properties: { publisher: 'Microsoft.GuestConfiguration' - type: 'ConfigurationforLinux' + type: 'ConfigurationForLinux' typeHandlerVersion: '1.0' autoUpgradeMinorVersion: true enableAutomaticUpgrade: true