Azure DevOps - Workload Identity Federation
In a previous blog post, I discussed Workload Identity Federation in AKS, the successor to the Azure Pod Identity solutions and a more elegant
In a previous post I talked about Operations Management Suite aka Log Analytics and how it can help you achieve a better understanding of what’s happening on your on-premise and cloud resources. In this blog post I will show you how to enroll your Azure VMs in OMS.
There are four ways you can enroll your Azure virtual machines in OMS:
PowerShell. (Saw that coming didn’t you?)
Installing OMS Agent at provisioning time using an ARM JSON Template.
Connecting the Azure VMs to OMS from the portal.
Manually installing the Windows / Linux agent.
Let’s tackle the first three scenarios, shall we?
1. To connect your Azure VMs to OMS, I created a very simple interactive script that can help you do this operation with ease. Make sure that you have the latest Azure PS cmdlets installed.
function script:Connect-AzureVMstoOMS { <# .SYNOPSIS This script will allow you to en-roll multiple or all VMs from your Azure Subscription in Operations Management Suite .DESCRIPTION The purpose of this script is to ease the enrollment process of Azure VMs in in a specific OMS account. The script requires the user to already be logged in on his Azure subscription. The script works with Windows or Linux VMs If the currently selected Azure Subscription does not have an OMS account then the user has the option to provide a OMS WorkSpace ID and a WorkSpace Key. .PARAMETER SelectionMethod Mandatory Parameter -Selection Method is used to select which VMs you want be en-rolled in OMS or all of them. Mandatory Values = Multiple / All .PARAMETER OMSWorkSpaceID Optional Parameter -If an OMS account doesn't exist in the specified Azure Subscription then a Workspace ID is required. .PARAMETER OMSWorkSpaceKey Optional Parameter -If an OMS account doesn't exist in the specified Azure Subscription then a Workspace Key is required. .EXAMPLE Connect-AzureVMstoOM -SelectionMethod Multiple -OMSWorkSpaceID Value -OMSWorkSpaceKey Value The script is executed and prompts the user which VMs he should en-roll in a OMS account which was provided Connect-AzureVMstoOM -SelectionMethod All The script will en-roll all the VMs from the selected Azure subscription in the OMS account that's present in the subscription. #> param( [Parameter(Mandatory = $true,HelpMessage = 'The input for this parameter is Multiple or All. The valueallows you to select a number of VMs that you want to enroll in OMS. The value will enroll all VMs Windows / Linux VMs in OMS')] [ValidateSet('Multiple','All')] [string] $SelectionMethod, [String] $OMSWorkSpaceID, [String] $OMSWorkSpaceKey ) try { $Subscription = Get-AzureRmContext } catch { Write-Error -Message "No Azure Context was found. Please run Login-AzureRmAccount and select an Azure subscription" } if(($OMSWorkSpaceID -eq '') -and ($OMSWorkSpaceKey -eq '')) { if ((Get-AzureRmOperationalInsightsWorkspace).Count -gt 1) { $OMSWorkSpace = Get-AzureRmOperationalInsightsWorkspace | Out-GridView -OutputMode Single } else { $OMSWorkSpace = Get-AzureRmOperationalInsightsWorkspace } if ($OMSWorkSpace -eq $null) { Write-Error -Message ('Cannot find an OMS account in the current subscription. Subscription Name = {0} Subscription ID = {1}' ` -f $Subscription.Subscription.SubscriptionName, ` $Subscription.Subscription.SubscriptionID) ` -Category ObjectNotFound break } $OMSWorkSpaceID = $OMSWorkSpace.CustomerId $OMSWorkSpaceKey = (Get-AzureRmOperationalInsightsWorkspaceSharedKeys ` -ResourceGroupName $OMSWorkSpace.ResourceGroupName ` -Name $OMSWorkSpace.Name).PrimarySharedKey } switch ($SelectionMethod){ 'Multiple' { $VMs = Get-AzureRmVM | Out-GridView -OutputMode Multiple } 'All' { $VMs = Get-AzureRmVM } } foreach ($VM in $VMs) { if ($VM.StorageProfile.OsDisk.OsType -eq 'Windows') { Write-Verbose -Message ('Installing Windows OMS Agent on {0}' -f $VM.Name) Set-AzureRmVMExtension -ResourceGroupName $VM.ResourceGroupName ` -VMName $VM.Name ` -Name 'MicrosoftMonitoringAgent' ` -Publisher 'Microsoft.EnterpriseCloud.Monitoring' ` -ExtensionType 'MicrosoftMonitoringAgent' ` -TypeHandlerVersion '1.0' ` -Location $VM.Location ` -SettingString "{'workspaceId': '$OMSWorkSpaceID'}" ` -ProtectedSettingString "{'workspaceKey': '$OMSWorkSpaceKey'}" } elseif ($VM.StorageProfile.OsDisk.OsType -eq 'Linux') { Write-Verbose -Message ('Installing Linux OMS Agent on {0}.Name' -f $VM.Name) Set-AzureRmVMExtension -ResourceGroupName $VM.ResourceGroupName ` -VMName $VM.Name -Name 'OmsAgentForLinux' ` -Publisher 'Microsoft.EnterpriseCloud.Monitoring' ` -ExtensionType 'OmsAgentForLinux' ` -TypeHandlerVersion '1.0' ` -Location $VM.Location ` -SettingString "{'workspaceId': '$OMSWorkSpaceID'}" ` -ProtectedSettingString "{'workspaceKey': '$OMSWorkSpaceKey'}" } } }
2. To enroll a VM at provisioning time you need to deploy it using an ARM Template. I suggest using Visual Studio with the latest Azure SDK in order to ease the template creation process. With Visual Studio you have a graphical interface that allows you to add and nest resources as you please. Try it, you will love it ?
Here’s an example ARM template that handles both Linux and Windows scenarios:
{ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { "workspaceId": { "type": "string", "metadata": { "description": "Workspace ID" } }, "workspaceKey": { "type": "securestring", "metadata": { "description": "Workspace Key" } }, "vmName": { "type": "string", "metadata": { "description": "Virtual Machine name." } }, "osType": { "type": "string", "allowedValues": [ "Windows", "Linux" ], "metadata": { "description": "This is the OS that your VM will be running" } }, "adminUsername": { "type": "string", "metadata": { "description": "User name for the Virtual Machine." } }, "adminPassword": { "type": "securestring", "metadata": { "description": "Password for the Virtual Machine." } }, "dnsLabelPrefix": { "type": "string", "metadata": { "description": "DNS Label for the Public IP. Must be lowercase. It should match with the following regular expression: ^[a-z][a-z0-9-]{1,61}[a-z0-9]$ or it will raise an error." } } }, "variables": { "computeApiVersion": "2016-03-30", "networkApiVersion": "2016-06-01", "storageApiVersion": "2016-01-01", "location": "[resourceGroup().location]", "storageAccountType": "Standard_LRS", "saVhdName": "[concat(parameters('vmName'), uniqueString(resourceGroup().id))]", "nicName": "[concat(parameters('vmName'), '.vmNIC')]", "addressPrefix": "10.0.0.0/16", "subnetName": "Subnet", "subnetPrefix": "10.0.0.0/24", "publicIPAddressName": "[concat(parameters('vmName'), '.PublicIP')]", "publicIPAddressType": "Dynamic", "vmSize": "Standard_D1_v2", "virtualNetworkName": "[concat(parameters('vmName'), '.VNET')]", "vnetID": "[resourceId('Microsoft.Network/virtualNetworks',variables('virtualNetworkName'))]", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", "Windows": { "imagePublisher": "MicrosoftWindowsServer", "imageOffer": "WindowsServer", "imageVersion": "2016-Datacenter", "omsType": "MicrosoftMonitoringAgent" }, "Linux": { "imagePublisher": "Canonical", "imageOffer": "UbuntuServer", "imageVersion": "16.04.0-LTS", "omsType": "OmsAgentForLinux" }, "OMSSettings": "[variables(parameters('osType'))]" }, "resources": [ { "apiVersion": "[variables('storageApiVersion')]", "type": "Microsoft.Storage/storageAccounts", "name": "[variables('saVhdName')]", "location": "[variables('location')]", "sku": { "name": "[variables('storageAccountType')]" }, "kind": "Storage", "properties": {} }, { "apiVersion": "[variables('networkApiVersion')]", "type": "Microsoft.Network/publicIPAddresses", "name": "[variables('publicIPAddressName')]", "location": "[resourceGroup().location]", "properties": { "publicIPAllocationMethod": "Dynamic", "dnsSettings": { "domainNameLabel": "[parameters('dnsLabelPrefix')]" } } }, { "apiVersion": "[variables('networkApiVersion')]", "type": "Microsoft.Network/virtualNetworks", "name": "[variables('virtualNetworkName')]", "location": "[resourceGroup().location]", "properties": { "addressSpace": { "addressPrefixes": [ "[variables('addressPrefix')]" ] }, "subnets": [ { "name": "[variables('subnetName')]", "properties": { "addressPrefix": "[variables('subnetPrefix')]" } } ] } }, { "apiVersion": "[variables('networkApiVersion')]", "type": "Microsoft.Network/networkInterfaces", "name": "[variables('nicName')]", "location": "[resourceGroup().location]", "dependsOn": [ "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "properties": { "ipConfigurations": [ { "name": "ipconfig1", "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { "id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" } } } ] } }, { "apiVersion": "[variables('computeApiVersion')]", "type": "Microsoft.Compute/virtualMachines", "name": "[parameters('vmName')]", "location": "[resourceGroup().location]", "dependsOn": [ "[concat('Microsoft.Storage/storageAccounts/', variables('saVhdName'))]", "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" ], "properties": { "hardwareProfile": { "vmSize": "[variables('vmSize')]" }, "osProfile": { "computername": "[parameters('vmName')]", "adminUsername": "[parameters('adminUsername')]", "adminPassword": "[parameters('adminPassword')]" }, "storageProfile": { "imageReference": { "publisher": "[variables('OMSSettings').imagePublisher]", "offer": "[variables('OMSSettings').imageOffer]", "sku": "[variables('OMSSettings').imageVersion]", "version": "latest" }, "osDisk": { "name": "osdisk", "vhd": { "uri": "[concat('http://', variables('saVhdName'), '.blob.core.windows.net/vhds/', concat(concat( parameters('vmName') ), '-osdisk.vhd'))]" }, "caching": "ReadWrite", "createOption": "FromImage" } }, "networkProfile": { "networkInterfaces": [ { "id": "[resourceId('Microsoft.Network/networkInterfaces',variables('nicName'))]" } ] }, "diagnosticsProfile": { "bootDiagnostics": { "enabled": "true", "storageUri": "[concat('http://', variables('saVhdName'), '.blob.core.windows.net')]" } } }, "resources": [ { "name": "Microsoft.EnterpriseCloud.Monitoring", "type": "extensions", "location": "[resourceGroup().location]", "apiVersion": "2015-06-15", "dependsOn": [ "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" ], "tags": { "displayName": "OMSEnroll" }, "properties": { "publisher": "Microsoft.EnterpriseCloud.Monitoring", "type": "[variables('OMSSettings').omsType]", "typeHandlerVersion": "1.0", "autoUpgradeMinorVersion": true, "settings": { "workspaceId": "[parameters('workspaceId')]" }, "protectedSettings": { "workspaceKey": "[parameters('workspaceKey')]" } } } ] } ] }
3. Enrolling VMs from the Azure Portal is very a very simple operation. You basically go to your Log Analytics resource located in your Azure subscription, select Virtual Machines and press connect on each virtual machine that shows the status “Not Connected”. The downside of doing it this way, is that if you have a lot of VMs that you want to enroll in your Log Analytics account, then it will take a lot of clicking.