Remove-VMExtension.ps1
<#PSScriptInfo
.VERSION 1.0 .GUID 5d26f91b-5975-45f5-baa7-384b4276a155 .AUTHOR dougbrad@microsoft.com .COMPANYNAME .COPYRIGHT .TAGS .LICENSEURI .PROJECTURI .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES .PRIVATEDATA #> <# .SYNOPSIS This script removes Log Analytics and Dependency Agent VM extensions from VM's and VM Scale Sets .DESCRIPTION This script removes Log Analytics and Dependency Agent VM extensions from VM's and VM Scale Sets Specify what to apply to with following parameters: -LogAnalyticsAgent and/or -DependencyAgent and/or -TriggerVmssManualVMUpdate Can be applied to: - Subscription - Resource Group in a Subscription - Specific VM/VM Scale Set - Compliance results of a policy for a VM or VM Extension Script will show you list of VM's/VM Scale Sets that will apply to and let you confirm to continue. Use -Approve switch to run without prompting, if all required parameters are provided. Use -WhatIf if you would like to see what would happen in terms of installs, what workspace configured to, and status of the extension. .PARAMETER SubscriptionId SubscriptionId for the VMs/VM Scale Sets If using PolicyAssignmentName parameter, subscription that VM's are in .PARAMETER ResourceGroup <Optional> Resource Group to which the VMs or VM Scale Sets belong to .PARAMETER Name <Optional> To install to a single VM/VM Scale Set .PARAMETER PolicyAssignmentName <Optional> Take the input VM's to operate on as the Compliance results from this Assignment If specified will only take from this source. .PARAMETER LogAnalyticsAgent <Optional> Remove the Log Analytics Agent extension .PARAMETER DependencyAgent <Optional> Remove the Dependency Agent extension .PARAMETER ApplyToVM <Optional> Apply operation to VMs .PARAMETER ApplyToVMSS <Optional> Apply operation to VM Scale Sets .PARAMETER TriggerVmssManualVMUpdate <Optional> Set this flag to trigger update of VM instances in a scale set whose upgrade policy is set to Manual .PARAMETER Approve <Optional> Gives the approval with no confirmation prompt for the listed VM's/VM Scale Sets .PARAMETER Whatif <Optional> See what would happen .PARAMETER Confirm <Optional> Confirm every action .EXAMPLE .\Remove-VMExtension.ps1 -SubscriptionId <sub id> -ApplyToVM -LogAnalyticsAgent -DependencyAgent Removes both Log Analytics and Dependency Agent extension for all VM's in subscription .EXAMPLE .\Remove-VMExtension.ps1 -SubscriptionId <sub id> -PolicyAssignmentName 4a69c0a045e94d88bf72715a -DependencyAgent -LogAnalyticsAgent Remove both Log Analytics and Dependency Agent extension for VM's not compliant with this policy .LINK This script is posted to and further documented at the following location: http://aka.ms/OnBoardVMInsights #> [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(mandatory = $true)][string]$SubscriptionId, [Parameter(mandatory = $false)][string]$ResourceGroup, [Parameter(mandatory = $false)][string]$Name, [Parameter(mandatory = $false)][string]$PolicyAssignmentName, [Parameter(mandatory = $false)][switch]$LogAnalyticsAgent, [Parameter(mandatory = $false)][switch]$DependencyAgent, [Parameter(mandatory = $false)][switch]$ApplyToVM, [Parameter(mandatory = $false)][switch]$ApplyToVMSS, [Parameter(mandatory = $false)][switch]$TriggerVmssManualVMUpdate, [Parameter(mandatory = $false)][switch]$Approve ) # # FUNCTIONS # function Get-VMExtension { <# .SYNOPSIS Return the VM extension of specified ExtensionType #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)][string]$VMName, [Parameter(mandatory = $true)][string]$vmResourceGroupName, [Parameter(mandatory = $true)][string]$ExtensionType ) $vm = Get-AzureRmVM -Name $VMName -ResourceGroupName $vmResourceGroupName -DisplayHint Expand $extensions = $vm.Extensions foreach ($extension in $extensions) { if ($ExtensionType -eq $extension.VirtualMachineExtensionType) { Write-Verbose("$VMName : Extension: $ExtensionType found on VM") $extension return } } Write-Verbose("$VMName : Extension: $ExtensionType not found on VM") } function Remove-VMExtension { <# .SYNOPSIS Remove VM Extension #> [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $true)][string]$VMName, [Parameter(mandatory = $true)][string]$VMResourceGroupName, [Parameter(mandatory = $true)][string]$ExtensionType, [Parameter(mandatory = $true)][hashtable]$OperationStatus ) $extension = Get-VMExtension -VMName $VMName -VMResourceGroup $VMResourceGroupName -ExtensionType $ExtensionType if ($extension) { $message = "$VMName : $ExtensionType extension with name " + $extension.Name + " installed. Provisioning State: " + $extension.ProvisioningState + " " + $extension.Settings Write-Output($message) if ($PSCmdlet.ShouldProcess($VMName, "remove extension $ExtensionType")) { Write-Output("$VMName : Removing extension $ExtensionType") $removeResult = Remove-AzureRmVMExtension -ResourceGroupName $VMResourceGroupName -VMName $VMName -Name $extension.Name -Force if ($removeResult -and $removeResult.IsSuccessStatusCode) { $message = "$VMName : Successfully removed $ExtensionType" $OperationStatus.Succeeded += $message Write-Output($message) } else { $message = "$VMName : Failed to remove $ExtensionType" Write-Warning($message) $OperationStatus.Failed += $message } } } else { $message = "$VMName : Extension: $ExtensionType not found on VM" Write-Output($message) $OperationStatus.ExtensionNotFound += $message } } function Get-VMssExtension { <# .SYNOPSIS Return the VM extension of specified ExtensionType #> [CmdletBinding()] param ( [Parameter(Mandatory = $True)][System.Object]$VMss, [Parameter(mandatory = $true)][string]$ExtensionType ) foreach ($extension in $VMss.VirtualMachineProfile.ExtensionProfile.Extensions) { if ($ExtensionType -eq $extension.Type) { Write-Verbose("$VMScaleSetName : Extension: $ExtensionType found on VMSS") $extension return } } Write-Verbose("$VMScaleSetName : Extension: $ExtensionType not found on VMSS") } function Remove-VMssExtension { <# .SYNOPSIS Remove VM Extension based on ExtensionType #> [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $True)][string]$VMScaleSetName, [Parameter(Mandatory = $True)][string]$VMScaleSetResourceGroupName, [Parameter(Mandatory = $True)][string]$ExtensionType ) $scalesetObject = Get-AzureRMVMSS -VMScaleSetName $VMScaleSetName -ResourceGroupName $VMScaleSetResourceGroupName $extension = Get-VMssExtension -VMss $scalesetObject -ExtensionType $ExtensionType if ($extension) { Write-Output("$VMScaleSetName : $ExtensionType extension with name " + $extension.Name + " installed. Provisioning State: " + $extension.ProvisioningState + " " + $extension.Settings) if ($PSCmdlet.ShouldProcess($VMScaleSetName, "install extension $ExtensionType")) { Write-Verbose("$VMScaleSetName : Removing $ExtensionType with name $extensionName") $result = Remove-AzureRmVmssExtension -VirtualMachineScaleSet $scalesetObject -Name $extension.Name if ($result -and $result.ProvisioningState -eq "Succeeded") { Write-Output("$VMScaleSetName : Succeeded removing $ExtensionType extension") } else { $message = "$VMScaleSetName : failed removing $ExtensionType extension" Write-Warning($message) $OperationStatus.Failed += $message return } Write-Output("$VMScaleSetName : Updating scale set") $result = Update-AzureRmVmss -VMScaleSetName $VMScaleSetName -ResourceGroupName $VMScaleSetResourceGroupName -VirtualMachineScaleSet $scalesetObject if ($result -and $result.ProvisioningState -eq "Succeeded") { $message = "$VMScaleSetName : Successfully updated scale set" Write-Output($message) $OperationStatus.Succeeded += $message } else { $message = "$VMScaleSetName : failed updating scale set" Write-Warning($message) $OperationStatus.Failed += $message } } } else { $message = "$VMScaleSetName : Extension: $ExtensionType not found on VMSS" Write-Output($message) $OperationStatus.ExtensionNotFound += $message } } # # Main Script # # # First make sure authenticed and Select the subscription supplied # $account = Get-AzureRmContext if ($null -eq $account.Account) { Write-Output("Account Context not found, please login") Login-AzureRmAccount -subscriptionid $SubscriptionId } else { if ($account.Subscription.Id -eq $SubscriptionId) { Write-Verbose("Subscription: $SubscriptionId is already selected.") $account } else { Write-Output("Current Subscription:") $account Write-Output("Changing to subscription: $SubscriptionId") Select-AzureRmSubscription -SubscriptionId $SubscriptionId } } $VMs = @() $ScaleSets = @() # To report on overall status $ExtensionNotFound = @() $OnboardingSucceeded = @() $OnboardingFailed = @() $OnboardingBlockedNotRunning = @() $VMScaleSetNeedsUpdate = @() $VMScaleSetInstancesUpdated = @() $OperationStatus = @{ ExtensionNotFound = $ExtensionNotFound; Succeeded = $OnboardingSucceeded; Failed = $OnboardingFailed; NotRunning = $OnboardingBlockedNotRunning; VMScaleSetNeedsUpdate = $VMScaleSetNeedsUpdate; VMScaleSetInstancesUpdated = $VMScaleSetInstancesUpdated; } # Log Analytics Extension constants $MMAExtensionMap = @{ "Windows" = "MicrosoftMonitoringAgent"; "Linux" = "OmsAgentForLinux" } # Dependency Agent Extension constants $DAExtensionMap = @{ "Windows" = "DependencyAgentWindows"; "Linux" = "DependencyAgentLinux" } if ($PolicyAssignmentName) { Write-Output("Getting list of VM's from PolicyAssignmentName: " + $PolicyAssignmentName) $complianceResults = Get-AzureRmPolicyState -PolicyAssignmentName $PolicyAssignmentName foreach ($result in $complianceResults) { Write-Verbose($result.ResourceId) Write-Verbose($result.ResourceType) if ($result.SubscriptionId -ne $SubscriptionId) { Write-Output("VM is not in same subscription, this scenario is not currently supported. Skipping this VM.") } $vmName = $result.ResourceId.split('/')[8] $vmResourceGroup = $result.ResourceId.split('/')[4] # Skip if ResourceGroup or Name provided, but does not match if ($ResourceGroup -and $ResourceGroup -ne $vmResourceGroup) { continue } if ($Name -and $Name -ne $vmName) { continue } $vm = Get-AzureRmVM -Name $vmName -ResourceGroupName $vmResourceGroup $vmStatus = Get-AzureRmVM -Status -Name $vmName -ResourceGroupName $vmResourceGroup # fix to have same property as VM that is retrieved without Name $vm | Add-Member -NotePropertyName PowerState -NotePropertyValue $vmStatus.Statuses[1].DisplayStatus $VMs = @($VMs) + $vm } } if (! $PolicyAssignmentName) { Write-Output("Getting list of VM's or VM ScaleSets matching criteria specified") if (!$ResourceGroup -and !$Name) { # If ResourceGroup value is not passed - get all VMs under given SubscriptionId if ($ApplyToVM) { $VMs = Get-AzureRmVM -Status } if ($ApplyToVMSS) { $ScaleSets = Get-AzureRmVmss } $VMs = @($VMs) + $ScaleSets } else { # If ResourceGroup value is passed - select all VMs under given ResourceGroupName if ($ApplyToVM) { $VMs = Get-AzureRmVM -ResourceGroupName $ResourceGroup -Status if ($Name) { $VMs = $VMs | Where-Object {$_.Name -like $Name} } } if ($ApplyToVMSS) { $ScaleSets = Get-AzureRmVmss -ResourceGroupName $ResourceGroup if ($Name) { $ScaleSets = $ScaleSets | Where-Object {$_.Name -like $Name} } } $VMs = @($VMs) + $ScaleSets } } Write-Output("`nVM's or VM ScaleSets matching criteria:`n") $VMS | ForEach-Object { Write-Output ($_.Name + " " + $_.PowerState) } if (-not ($LogAnalyticsAgent -or $DependencyAgent -or $TriggerVmssManualVMUpdate)) { Write-Output "`nPlease provide parameter for extension to remove. Either -LogAnalyticsAgent and/or -DependencyAgent" return } # Validate customer wants to continue Write-Output("`nThis operation will remove the extensions as per arguments on above $($VMS.Count) VM's or VM Scale Sets.") Write-Output("VM's in a non-running state will be skipped.") if ($Approve -eq $true -or !$PSCmdlet.ShouldProcess("All") -or $PSCmdlet.ShouldContinue("Continue?", "")) { Write-Output "" } else { Write-Output "You selected No - exiting" return } # # Loop through each VM/VM Scale set, as appropriate handle installing VM Extensions # Foreach ($vm in $VMs) { # set as variabels so easier to use in output strings $vmName = $vm.Name $vmLocation = $vm.Location $vmResourceGroupName = $vm.ResourceGroupName # # Find OS Type # if ($vm.type -eq 'Microsoft.Compute/virtualMachineScaleSets') { $isScaleset = $true $scalesetVMs = @() $scalesetVMs = Get-AzureRmVMssVM -ResourceGroupName $vmResourceGroupName -VMScaleSetName $vmName if ($scalesetVMs.length -gt 0) { if ($scalesetVMs[0]) { $osType = $scalesetVMs[0].storageprofile.osdisk.ostype } } } else { $isScaleset = $false $osType = $vm.StorageProfile.OsDisk.OsType } # # Map to correct extension for OS type # $mmaExt = $MMAExtensionMap.($osType.ToString()) if (! $mmaExt) { Write-Warning("$vmName : has an unsupported OS: $osType") continue } $daExt = $DAExtensionMap.($osType.ToString()) Write-Verbose("Settings: ") Write-Verbose("ResourceGroup: $vmResourceGroupName") Write-Verbose("VM: $vmName") Write-Verbose("Location: $vmLocation") Write-Verbose("OS Type: $ext") Write-Verbose("Dependency Agent: $daExt, HandlerVersion: $daExtVersion") Write-Verbose("Monitoring Agent: $mmaExt, HandlerVersion: $mmaExtVersion") if ($isScaleset) { if ($LogAnalyticsAgent) { Remove-VMssExtension ` -VMScaleSetName $vmName ` -VMScaleSetResourceGroupName $vmResourceGroupName ` -ExtensionType $mmaExt } if ($DependencyAgent) { Remove-VMssExtension ` -VMScaleSetName $vmName ` -VMScaleSetResourceGroupName $vmResourceGroupName ` -ExtensionType $daExt } $scalesetObject = Get-AzureRMVMSS -VMScaleSetName $vmName -ResourceGroupName $vmResourceGroupName if ($scalesetObject.UpgradePolicy.mode -eq 'Manual') { if ($TriggerVmssManualVMUpdate -eq $true) { Write-Output("$vmName : Upgrading scale set instances since the upgrade policy is set to Manual") $scaleSetInstances = @{} $scaleSetInstances = Get-AzureRMVMSSvm -ResourceGroupName $vmResourceGroupName -VMScaleSetName $vmName -InstanceView $i = 0 $instanceCount = $scaleSetInstances.Length Foreach ($scaleSetInstance in $scaleSetInstances) { $i++ Write-Output("$vmName : Updating instance " + $scaleSetInstance.Name + " $i of $instanceCount") Update-AzureRmVmssInstance -ResourceGroupName $vmResourceGroupName -VMScaleSetName $vmName -InstanceId $scaleSetInstance.InstanceId } $message = "$vmName All scale set instances upgraded" Write-Output($message) $OperationStatus.VMScaleSetInstancesUpdated += $message } else { $message = "$vmName : has UpgradePolicy of Manual. Please trigger upgrade of VM Scale Set or call with -TriggerVmssManualVMUpdate" Write-Warning($message) $OperationStatus.VMScaleSetNeedsUpdate += $message } } } # # Handle VM's # else { if ("VM Running" -ne $vm.PowerState) { $message = "$vmName : has a PowerState " + $vm.PowerState + " Skipping" Write-Output($message) $OperationStatus.NotRunning += $message continue } if ($LogAnalyticsAgent) { Remove-VMExtension ` -VMName $vmName ` -VMResourceGroupName $vmResourceGroupName ` -ExtensionType $mmaExt ` -OperationStatus $OperationStatus } if ($DependencyAgent) { Remove-VMExtension ` -VMName $vmName ` -VMResourceGroupName $vmResourceGroupName ` -ExtensionType $daExt ` -OperationStatus $OperationStatus } # Write-Output("`n") } } Write-Output("`nSummary:") Write-Output("`nExtension not enabled: (" + $OperationStatus.ExtensionNotFound.Count + ")") $OperationStatus.ExtensionNotFound | ForEach-Object { Write-Output ($_) } Write-Output("`nSucceeded: (" + $OperationStatus.Succeeded.Count + ")") $OperationStatus.Succeeded | ForEach-Object { Write-Output ($_) } Write-Output("`nNot running - start VM to configure: (" + $OperationStatus.NotRunning.Count + ")") $OperationStatus.NotRunning | ForEach-Object { Write-Output ($_) } Write-Output("`nVM Scale Set needs update: (" + $OperationStatus.VMScaleSetNeedsUpdate.Count + ")") $OperationStatus.VMScaleSetNeedsUpdate | ForEach-Object { Write-Output ($_) } Write-Output("`nVM Scale Set instances updated: (" + $OperationStatus.VMScaleSetInstancesUpdated.Count + ")") $OperationStatus.VMScaleSetInstancesUpdated | ForEach-Object { Write-Output ($_) } Write-Output("`nFailed: (" + $OperationStatus.Failed.Count + ")") $OperationStatus.Failed | ForEach-Object { Write-Output ($_) } |