Get-AzureVmManagementSolutionOnboardingState.ps1

<#PSScriptInfo
 
.VERSION 1.0.9
 
.GUID 3268e204-99af-4846-8aa6-1a210e011bce
 
.AUTHOR Stas Kuvshinov
 
.COMPANYNAME Microsoft Corporation
 
.COPYRIGHT Microsoft Corporation
 
.TAGS Automation Management
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES LogAnalyticsQuery
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
The LogAnalyticsQuery module is available on https://dev.loganalytics.io/documentation/Tools/PowerShell-Cmdlets
 
.PRIVATEDATA
 
#>
 

#Requires -Module @{ ModuleName = 'AzureRM.Profile'; ModuleVersion = '3.1.0' }
#Requires -Module @{ ModuleName = 'AzureRM.Compute'; ModuleVersion = '3.1.0' }
#Requires -Module @{ ModuleName = 'AzureRM.Resources'; ModuleVersion = '3.1.0' }
#Requires -Module @{ ModuleName = 'AzureRM.OperationalInsights'; ModuleVersion = '3.4.1' }

<#
 
.Parameter vmResourceId
Required. Full resource identifier of an Azure VM
 
.Parameter solutionType
Required. Type of the management solution. Possible values: 'updates', 'changeTracking'
 
.Parameter queryIntervalHours
Optional. Query interval window in hours to use when looking for ingested data. (Default = 24)
 
.EXAMPLE
.\Get-AzureVmManagementSolutionOnboardingState.ps1 -vmResourceId /subscriptions/6c098ac2-6c43-4fd8-875c-6d089beb2ef5/resourceGroups/RG1/providers/Microsoft.Compute/virtualMachines/stas-ubuntu-1604-x -solutionType updates
 
.EXAMPLE
.\Get-AzureVmManagementSolutionOnboardingState.ps1 -vmResourceId /subscriptions/6c098ac2-6c43-4fd8-875c-6d089beb2ef5/resourceGroups/RG1/providers/Microsoft.Compute/virtualMachines/stas-ubuntu-1604-x -solutionType updates -queryIntervalHours 12
 
.Example
.\Get-AzureVmManagementSolutionOnboardingState.ps1 -vmResourceId /subscriptions/6c098ac2-6c43-4fd8-875c-6d089beb2ef5/resourceGroups/RG1/providers/Microsoft.Compute/virtualMachines/ws-2012r2-a4 -solutionType changeTracking
 
.DESCRIPTION
 The script gets management solution onboarding information for an Azure VM.
 Please run it in the authenticated Azure session, after calling the 'Login-AzureRmAccount' and 'Select-AzureRMSubscription' commands with required parameters.
 See the following docs for references:
  https://docs.microsoft.com/en-us/powershell/azure/authenticate-azureps
  https://docs.microsoft.com/en-us/powershell/azure/manage-subscriptions-azureps
  
 Most of the script dependencies are available at the PowerShell Gallery with the only exception of the LogAnalyticsQuery.psm1 module that you need to manually download from https://dev.loganalytics.io/documentation/Tools/PowerShell-Cmdlets.
#>
 
Param(
    [string]
    [Parameter(Mandatory = $true)]
    $vmResourceId,

    [string]
    [Parameter(Mandatory = $true)]
    [ValidateSet("updates", "changeTracking")]
    $solutionType,

    [int]
    [Parameter(Mandatory = $false)]
    $queryIntervalHours = 24
)

$information = [pscustomobject] [ordered] @{
    'AzureVMFound'=$false
    'AzureVMHasManagementExtensionInstalled'='n/a'
    'AzureVMManagementExtensionHasManagementWorkspaceInformation'='n/a'
    'ManagementWorkspaceFoundInSubscription'='n/a'
    'VmHeartbeatDataIngestedIntoWorkspace'='n/a'
    'VmHeartbeatDataIndicatesThatVmHasSolutionEnabled'='n/a'
    'SolutionDataIngestedIntoWorkspace'='n/a'
}

$vmDiscriminator = New-Object -Type PSObject -Proper @{'id'=$vmResourceId}
$vm = $vmDiscriminator | Get-AzureRmResource
if($vm -ne $null){
    $information.AzureVMFound = $true

    $vmManagementExtensionResourceId = "$($vmResourceId)/extensions/MMAExtension"
    if($vm.Properties.osProfile.linuxConfiguration -ne $null){
        $vmManagementExtensionResourceId = "$($vmResourceId)/extensions/OMSAgentLinuxExtension"
    }
    $vmManagementExtensionDiscriminator = New-Object -Type PSObject -Proper @{'id'=$vmManagementExtensionResourceId}
    $vmManagementExtensionResource = $vmManagementExtensionDiscriminator | Get-AzureRmResource

    $information.AzureVMHasManagementExtensionInstalled = $false
    if($vmManagementExtensionResource -ne $null){
        $information.AzureVMHasManagementExtensionInstalled = $true

        $vmManagementWorkspaceId = $vmManagementExtensionResource.Properties.settings.workspaceId

        $information.AzureVMManagementExtensionHasManagementWorkspaceInformation = $false
        if($vmManagementWorkspaceId -ne $null){
            $information.AzureVMManagementExtensionHasManagementWorkspaceInformation = $true

            $vmManagementWorkspace = Get-AzureRmOperationalInsightsWorkspace | where-object {$_.CustomerId -eq $vmManagementWorkspaceId}

            $information.ManagementWorkspaceFoundInSubscription = $false
            if($vmManagementWorkspace -ne $null){
                $information.ManagementWorkspaceFoundInSubscription = $true

                $vmManagementWorkspaceResourceDiscriminator = New-Object -Type PSObject -Proper @{'id'=$vmManagementWorkspace.ResourceId}
                $vmManagementWorkspaceResource = $vmManagementWorkspaceResourceDiscriminator | Get-AzureRmResource

                $vmManagementWorkspaceSearchVersion = @{'0'='OQL';'1'='KQL'}[$vmManagementWorkspaceResource.Properties.features.searchVersion.ToString()]

                $queryVmHeartbeatDataIngestedIntoWorkspace = [String]::Format(@{
                'OQL'='Type=Heartbeat and (ResourceId="{0}" or Computer="{1}") | top 1'
                'KQL'='Heartbeat | where (ResourceId=~"{0}" or Computer=~"{1}") | take 1'
                }[$vmManagementWorkspaceSearchVersion], $vmResourceId, $vm.Name, $solutionType)

                $solutionTableName = @{'updates'='Update';'changeTracking'='ConfigurationChange'}[$solutionType]

                if($vmManagementWorkspaceSearchVersion -eq 'OQL'){
                    $endDate = (get-date)
                    $startDate = $endDate.AddHours(-1 * $queryIntervalHours)
                    $queryVmHeartbeatDataIngestedIntoWorkspace = [String]::Format('Type=Heartbeat and (ResourceId="{0}" or Computer="{1}") | top 1', $vmResourceId, $vm.Name, $solutionType)
                    $information.VmHeartbeatDataIngestedIntoWorkspace = ($vmManagementWorkspace | Get-AzureRmOperationalInsightsSearchResults -Query $queryVmHeartbeatDataIngestedIntoWorkspace -End $endDate -Start $startDate).Value.Count -eq 1                    

                    $queryVmHeartbeatDataIndicatesThatVmHasSolutionEnabled = [String]::Format('Type=Heartbeat and (ResourceId="{0}" or Computer="{1}") and Solutions:contains("{2}") | top 1', $vmResourceId, $vm.Name, $solutionType)
                    $information.VmHeartbeatDataIndicatesThatVmHasSolutionEnabled = ($vmManagementWorkspace | Get-AzureRmOperationalInsightsSearchResults -Query $queryVmHeartbeatDataIndicatesThatVmHasSolutionEnabled -End $endDate -Start $startDate).Value.Count -eq 1

                    $querySolutionDataIngestedIntoWorkspace = [String]::Format('Type={0} and Computer="{1}" | top 1', $solutionTableName, $vm.Name)
                    $information.SolutionDataIngestedIntoWorkspace = ($vmManagementWorkspace | Get-AzureRmOperationalInsightsSearchResults -Query $querySolutionDataIngestedIntoWorkspace -End $endDate -Start $startDate).Value.Count -eq 1
                }
                elseif($vmManagementWorkspaceSearchVersion -eq 'KQL'){
                    if(-not (Get-Command Invoke-LogAnalyticsQuery -ErrorAction SilentlyContinue)) {
                        Write-Error 'The VM is managed by a workspace with enhanced log analytics capabilities. Please, import the LogAnalyticsQuery module from https://dev.loganalytics.io/documentation/Tools/PowerShell-Cmdlets to verify if the solution data is being ingested into search.'
                        return $information
                    }

                    $queryVmHeartbeatDataIngestedIntoWorkspace = [String]::Format('Heartbeat | where TimeGenerated > ago(24h) and (ResourceId=~"{0}" or Computer=~"{1}") | take 1', $vmResourceId, $vm.Name, $solutionType)
                    $information.VmHeartbeatDataIngestedIntoWorkspace = (Invoke-LogAnalyticsQuery -WorkspaceName $vmManagementWorkspace.Name -ResourceGroup $vmManagementWorkspaceResource.ResourceGroupName -SubscriptionId $vmManagementWorkspaceResource.SubscriptionId -Query $queryVmHeartbeatDataIngestedIntoWorkspace).Results -ne $null

                    $queryVmHeartbeatDataIndicatesThatVmHasSolutionEnabled = [String]::Format('Heartbeat | where TimeGenerated > ago(24h) and (ResourceId=~"{0}" or Computer=~"{1}") and Solutions has "{2}" | take 1', $vmResourceId, $vm.Name, $solutionType)
                    $information.VmHeartbeatDataIndicatesThatVmHasSolutionEnabled = (Invoke-LogAnalyticsQuery -WorkspaceName $vmManagementWorkspace.Name -ResourceGroup $vmManagementWorkspaceResource.ResourceGroupName -SubscriptionId $vmManagementWorkspaceResource.SubscriptionId -Query $queryVmHeartbeatDataIndicatesThatVmHasSolutionEnabled).Results -ne $null

                    $querySolutionDataIngestedIntoWorkspace = [String]::Format('{0} | where TimeGenerated > ago(24h) and Computer=~"{1}" | take 1', $solutionTableName, $vm.Name)
                    $information.SolutionDataIngestedIntoWorkspace = (Invoke-LogAnalyticsQuery -WorkspaceName $vmManagementWorkspace.Name -ResourceGroup $vmManagementWorkspaceResource.ResourceGroupName -SubscriptionId $vmManagementWorkspaceResource.SubscriptionId -Query $querySolutionDataIngestedIntoWorkspace).Results -ne $null
                }
            }
        }
    }
}

return $information