GuestConfigurationTroubleshooter.psm1
<# .SYNOPSIS Gets the properties of the GuestConfig Extension on a specified VM .DESCRIPTION This script gets and prints the properties of the GuestConfig Extension on a specified windows or linux vm. .PARAMETER ResourceGroupName The Resource Group the VM is in .PARAMETER VMName The VM to get the GuestConfig Extension from .EXAMPLE PS> Get-VmGuestConfigExtensionProperties -ResourceGroupName "MyResourceGroup" -VMName "MyVM" #> function Get-VmGuestConfigExtensionProperties { [CmdletBinding()] param( [parameter(ParameterSetName = "Scoped", Mandatory=$true)][ValidateNotNullOrEmpty()][String]$ResourceGroupName, [parameter(ParameterSetName = "Scoped", Mandatory=$true)][ValidateNotNullOrEmpty()][String]$VMName, [parameter(ParameterSetName = "NonScoped", Mandatory=$true)][ValidateNotNullOrEmpty()]$VM ) $returnObject = [PSCustomObject](@{Name = "GuestConfigurationExtension"; Status = $true}) #Get the VM to check whether it is linux or windows if(-not($VM)) { $vm = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $VMName -ErrorAction SilentlyContinue } $name = "AzurePolicyForWindows" if(-not($vm)) { throw "The given VM could not be found in that Resource Group" } #If it is a windows machine if($vm.OSProfile.WindowsConfiguration) { #Get the extension $ext = Get-AzVMExtension -ResourceGroupName $ResourceGroupName -VMName $VMName -Name "AzurePolicyforWindows" -ErrorAction SilentlyContinue $testExt = Get-AzVMExtension -ResourceGroupName $ResourceGroupName -VMName $VMName -Name "AzurePolicyforLinux" -ErrorAction SilentlyContinue if($testExt) { Write-Warning "This Linux VM has the Windows version of the Guest Configuration Extension installed" $returnObject | Add-Member -NotePropertyName "Errors" -NotePropertyValue @() $returnObject.Errors += "That Windows VM has the Linux version of the Guest Configuration Extension installed" $returnObject.Status = $false return $returnTable } } #Otherwise if it is a linux machine elseif($vm.OSProfile.LinuxConfiguration) { #Get the extension $ext = Get-AzVMExtension -ResourceGroupName $ResourceGroupName -VMName $VMName -Name "AzurePolicyforLinux" -ErrorAction SilentlyContinue $name = "AzurePolicyForLinux" $testExt = Get-AzVMExtension -ResourceGroupName $ResourceGroupName -VMName $VMName -Name "AzurePolicyforWindows" -ErrorAction SilentlyContinue if($testExt) { Write-Warning "This Linux VM has the Windows version of the Guest Configuration Extension installed" $returnObject | Add-Member -NotePropertyName "Errors" -NotePropertyValue @() $returnObject.Errors += "That Linux VM has the Windows version of the Guest Configuration Extension installed" $returnObject.Status = $false return $returnObject } } else { Write-Warning "That VM's OS is not supported by Guest Configuration" $returnObject | Add-Member -NotePropertyName "Errors" -NotePropertyValue @() $returnObject.Errors += "That VM's OS is not supported by Guest Configuration" $returnObject.Status = $false return $returnObject } $returnObject | Add-Member -NotePropertyName "Details" -NotePropertyValue @{} $returnObject.Details['Extension'] = $ext Write-Verbose ("Checking for extension `"" + $name + "`"...") #If the extension is deployed if($ext) { Write-Verbose ("The extension `"" + $name + "`" was deployed successfully") #Check the provisioning state if($ext.ProvisioningState -eq "Succeeded") { Write-Verbose "Provisioning succeeded" } else { $warningString = "Provisioning did not succeed, provisioning state is: " + $ext.ProvisioningState Write-Warning $warningString $returnObject.Status = $false if($ext.ProvisioningState -eq "Not Started" -or $ext.ProvisioningState -eq "In Progress" -or $ext.ProvisioningState -eq "Updating") { $returnObject | Add-Member -NotePropertyName "Warnings" -NotePropertyValue @() $returnObject.Warnings += "Deployment for this extension is still in progress, wait for a couple minutes" } elseif($ext.ProvisioningState -eq "Failed") { $returnObject | Add-Member -NotePropertyName "Errors" -NotePropertyValue @() $returnObject.Errors += "Provisioning for this extension has failed, try running a remediation task" } } } else { $warningString = "The extension `"" + $name + "`" was not deployed successfully or was not deployed at all" Write-Warning $warningString $returnObject | Add-Member -NotePropertyName "Errors" -NotePropertyValue @() $returnObject.Errors += $warningString $returnObject.Status = $false } return $returnObject } <# .SYNOPSIS Gets the MSI Status of a VM .DESCRIPTION This script gets and returns the MSI Status of a specified VM in a specified Resource Group .PARAMETER ResourceGroupName The Resource Group the VM is in .PARAMETER VMName The VM to check the MSI status of .EXAMPLE PS> Test-VmMSIEnabled -ResourceGroupName "MyResourceGroup" -VMName "MyVM" #> function Get-VmMSIEnabled { [CmdletBinding()] param( [parameter(ParameterSetName = "Scoped", Mandatory=$true)][ValidateNotNullOrEmpty()][String]$ResourceGroupName, [parameter(ParameterSetName = "Scoped", Mandatory=$true)][ValidateNotNullOrEmpty()][String]$VMName, [parameter(ParameterSetName = "NonScoped", Mandatory=$true)][ValidateNotNullOrEmpty()]$VM ) $returnObj = [PSCustomObject](@{ Name = "MSI" Status = $true }) #Get the VM to check MSI status if(-not($VM)) { $vm = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $VMName -ErrorAction SilentlyContinue } #Check if the VM was found if(-not($vm)) { throw "The given VM could not be found in that Resource Group" } if($vm.Identity.Type -eq "SystemAssigned") { Write-Verbose "MSI is enabled on this VM" } else { Write-Warning "MSI is not enabled on this VM" Write-Warning "Enabling System Managed Identity is needed for Guest Configuration Policy scenarios" $returnObj | Add-Member -NotePropertyName "Errors" -NotePropertyValue @() $returnObj.Errors += "MSI is not enabled on this VM" $returnObj.Errors += "Enabling System Managed Identity is needed for Guest Configuration Policy scenarios" $returnObj.Status = $false } return $returnObj } function CheckAssignmentPermissions { param([parameter(Mandatory = $true)][ValidateNotNullOrEmpty()]$assignment, [parameter(Mandatory = $true)][ValidateNotNullOrEmpty()]$definition, [parameter(Mandatory = $false)][switch]$initiative) $roleIds = @() #If it's an initiative, get the role definition ids from each DINE policy in the initiative and store them in the roleIds array if($initiative) { for([int] $i = 0; $i -lt $definition.Properties.policyDefinitions.Count; ++$i) { $def = Get-AzPolicyDefinition -Id $definition.Properties.policyDefinitions[$i].policyDefinitionId if($def.Properties.policyRule.then.effect -eq "DeployIfNotExists") { foreach($id in $def.Properties.policyRule.then.details.roleDefinitionIds) { $roleIds += $id } } } } else { if($definition.Properties.policyRule.then.effect -eq "DeployIfNotExists") { foreach($id in $definition.Properties.policyRule.then.details.roleDefinitionIds) { $roleIds += $id } } } #Get all role assignments linked to this policy assignment $roleAssignments = Get-AzRoleAssignment -ObjectId $assignment.Identity.principalId -ErrorAction SilentlyContinue if($roleAssignments) { if($roleIds.Count -gt 0) { $success = $false $safeIndeces = @() if($roleAssignments.Count -gt 1) { for($i = 0; $i -lt $roleAssignments.Count; ++$i) { for($j = 0; $j -lt $roleIds.Count; ++$j) { #if they do match if($roleIds[$j].Contains($roleAssignments[$i].RoleDefinitionId)) { $safeIndeces += $i } } } if($safeIndeces.Count -eq $roleAssignments.Count) { $success = $true } } else { $success = $false for($i = 0; $i -lt $roleIds.Count; ++$i) { if($roleIds[$i].Contains($roleAssignments.RoleDefinitionId)) { $success = $true } } } if(-not($success)) { Write-Warning ("The policy `"" + $definition.Properties.displayName + "`" does not have the correct roles assigned to it") return ("This policy does not have the correct roles assigned to it") } } } else { Write-Warning ("The policy `"" + $definition.Properties.displayName + "`" does not have any roles assigned to it") return "This policy does not have any roles assigned to it" } return } #Check a policy condition template against a VM's image reference function CheckConditions { param( [parameter(Mandatory = $true)]$ImageRef, [parameter(Mandatory = $true)]$conditionTemplate ) #Get the anyOf to compare stuff with if($conditionTemplate.if.anyOf) { $toCheck = $conditionTemplate.if.anyOf } elseif($conditionTemplate.if.allOf) { foreach($condition in $conditionTemplate.if.allOf) { if($condition.anyOf) { $toCheck = $condition.anyOf break } } } foreach($condition in $toCheck) { if($condition.allOf) { $allOfStatus = $true foreach($condition2 in $condition.allOf) { Write-Verbose $condition2.field #set a compare property based on the field if($condition2.field -eq "Microsoft.Compute/imageSKU") { $compareProperty = $ImageRef.SKU } elseif($condition2.field -eq "Microsoft.Compute/imagePublisher") { $compareProperty = $ImageRef.Publisher } elseif($condition2.field -eq "Microsoft.Compute/imageOffer") { $compareProperty = $ImageRef.Offer } #Check all the possible conditions if($condition2.in) { Write-Verbose "in" if(-not($condition2.in.contains($compareProperty))) { $allOfStatus = $false } } elseif($condition2.notlike) { Write-Verbose "notlike" if($condition2.notLike -like $compareProperty) { $allOfStatus = $false } } elseif($condition2.notEquals) { Write-Verbose "notEquals" if($condition2.notEquals -eq $compareProperty) { $allOfStatus = $false } } elseif($condition2.like) { Write-Verbose "like" if($condition2.like -notlike $compareProperty) { $allOfStatus = $false } } elseif($condition2.equals) { Write-Verbose "equals" if($condition2.equals -ne $compareProperty) { $allOfStatus = $false } } } if($allOfStatus) { return $true } } else { if($condition.field -eq "Microsoft.Compute/imageSKU") { $compareProperty = $ImageRef.SKU } elseif($condition.field -eq "Microsoft.Compute/imagePublisher") { $compareProperty = $ImageRef.Publisher } elseif($condition.field -eq "Microsoft.Compute/imageOffer") { $compareProperty = $ImageRef.Offer } #Check all the possible conditions if($condition2.in) { Write-Verbose "in" if($condition2.in.contains($compareProperty)) { return $true } } elseif($condition2.notlike) { Write-Verbose "notlike" if($condition2.notLike -notlike $compareProperty) { return $true } } elseif($condition2.notEquals) { Write-Verbose "notEquals" if($condition2.notEquals -ne $compareProperty) { return $true } } elseif($condition2.like) { Write-Verbose "like" if($condition2.like -like $compareProperty) { return $true } } elseif($condition2.equals) { Write-Verbose "equals" if($condition2.equals -eq $compareProperty) { return $true } } } } return $false } <# .SYNOPSIS Returns a list of VM's that the passed definition (or any built-in Guest Configuration policies) could not be applied to .DESCRIPTION This cmdlet gets checks all of the imaging conditions of a windows and linux built-in Guest Configuration policies (or a passed policy definition) against a list of given VMs, and returns any VMs that did not pass the check .PARAMETER VMList The list of VMs to check (this should be a list of VM objects, not names) .PARAMETER PolicyDefinition This parameter allows you to check the imaging conditions of a specific policy instead of built-in in-guest policies .EXAMPLE PS> $vms = Get-AzVM -ResourceGroupName "MyResourceGroup" Get-NonApplicableVMs -VMList $vms .EXAMPLE PS> $vms = Get-AzVM -ResourceGroupName "MyResourceGroup" $def = Get-AzPolicyDefinition -Id "PolicyDefinitionGUID" Get-NonApplicableVMs -VMList $vms -PolicyDefinition $def #> function Get-NonApplicableVMs { [CmdletBinding()] param( [parameter(Mandatory = $true)][ValidateNotNullOrEmpty()]$VMList, [parameter()][ValidateNotNullOrEmpty()]$PolicyDefinition) $unapplicableList = @() #Use built-in definiton requirements $windowsTemplate = Get-Content -Path ($PSScriptRoot + "\WindowsConditionTemplate.json") | ConvertFrom-Json $linuxTemplate = Get-Content -Path ($PSScriptRoot + "\LinuxConditionTemplate.json") | ConvertFrom-Json #Check using windows template foreach($vm in $VMList) { Write-Verbose $vm.Name $currStatus = $false $imgRef = $vm.StorageProfile.ImageReference if($PolicyDefinition) { $currStatus = CheckConditions -ImageRef $imgRef -conditionTemplate $PolicyDefinition.Properties.policyRule } else { $currStatus = (CheckConditions -ImageRef $imgRef -conditionTemplate $windowsTemplate) -or (CheckConditions -ImageRef $imgRef -conditionTemplate $linuxTemplate) } if($currStatus -eq $false) { $unapplicableList += $vm.Name } } return $unapplicableList } <# .SYNOPSIS Prints warnings and errors for any Guest Configuration policies that were assigned incorrectly .DESCRIPTION This script checks all Guest Configuration policies in a given resource group or subscription and prints warnings and errors if any of them were assigned incorrectly .PARAMETER ResourceGroupName The Resource Group to check policies in .PARAMETER Subscription Switch parameter that allows you to check the policy assignments in the current subscription (Context based) .PARAMETER Force Switch parameter that allows you to skip any required input .EXAMPLE PS> Get-GuestConfigPolicyErrors -ResourceGroupName "MyResourceGroup" .EXAMPLE PS> Get-GuestConfigPolicyErrors -Subscription #> function Get-GuestConfigPolicyErrors { #this function is an abomination [CmdletBinding()] param( [parameter(ParameterSetName = "ResourceGroup", Mandatory=$true)] [ValidateNotNullOrEmpty()][String]$ResourceGroupName, [parameter(ParameterSetName = "Subscription", Mandatory=$true)][Switch]$CurrentSubscription, [parameter(Mandatory = $false, position = 1)][Switch]$Force ) if(-not($CurrentSubscription)) { $rg = Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue if(-not($rg)) { throw "The given Resource Group could not be found" } #Get all policies in the resource group $assignments = Get-AzPolicyAssignment -Scope $rg.ResourceId } else { $assignments = Get-AzPolicyAssignment } #Display time estimate, give option to continue/skip this test if there are at least 10 policies assigned #(Otherwise it should be short enough) if($assignments.Count -gt 10) { if(-not($Force) -and -not($PSCmdlet.ShouldContinue(("There are " + $assignments.Count + " policies assigned to this scope, so this test will take some time."), "Would you still like to continue?"))) { Write-Warning "User has chosen to skip this test" return @{Status = $false} } } #for tracking status $success = $true $totalErrors = 0 $guestConfigArray = @() $currGCCount = 0 $returnTable = @{} #If there are any policies if($assignments.Count -gt 0) { #Loop through each policy foreach($assignment in $assignments) { #Get the policies definition $id = $assignment.Properties.policyDefinitionId $verboseMessage = "Checking policy assignment `"" + $assignment.Properties.displayName + "`"" Write-Verbose $verboseMessage #Check if there is an id if($id) { $def = Get-AzPolicyDefinition -Id $id #Check if it is a Guest Configuration Policy if($def.Properties.metadata.category -eq "Guest Configuration") { Write-Verbose "Found Guest Configuration policy" $guestConfigCount += 1 $alreadyAdded = $false #Check if the policy is in an initiative if($def.Properties.policyDefinitions) { Write-Verbose "Policy was in an initiative" $permissionError = CheckAssignmentPermissions -assignment $assignment -definition $def -initiative if($permissionError) { ++$currGCCount $alreadyAdded = $true $guestConfigArray += ([PSCustomObject]@{ Name = $assignment.Properties.displayName Status = $false Warnings = @() Errors = @($permissionError) Details = @{ Assignment = $assignment Definition = $def Initiative = $true } }) } #Check to see if this initiative was assigned correctly if it is custom if($def.Properties.policyType -ne "BuiltIn") { $safeIndexes = @() #Nested for loop to compare each policy within the set for([int]$i = 0; $i -lt $def.Properties.policyDefinitions.Count; ++$i) { #status for current policy (starts as false, turns true when a match is found) $currentStatus = $false $iDef = Get-AzPolicyDefinition -Id $def.Properties.policyDefinitions[$i].policyDefinitionId for([int]$j = $i + 1; $j -lt $def.Properties.policyDefinitions.Count; ++$j) { $jDef = Get-AzPolicyDefinition -Id $def.Properties.policyDefinitions[$j].policyDefinitionId $effect1 = $iDef.Properties.policyRule.then.effect $effect2 = $jDef.Properties.policyRule.then.effect if($effect1 -eq "deployIfNotExists" -and ($effect2 -eq "audit" -or $effect2 -eq "auditIfNotExists")) { if($effect2 -eq "audit") { #loop through audit conditions to find name $auditAllOf = $jDef.Properties.policyRule.if.allOf foreach($condition in $auditAllOf) { if($condition.field -eq "name") { $auditName = $condition.equals break } } } else { $auditName = $jDef.Properties.policyRule.then.details.name } #compare names if($iDef.Properties.policyRule.then.details.name -eq $auditName) { $currentStatus = $true $safeIndexes += $j $safeIndexes += $i } } elseif(($effect1 -eq "audit" -or $effect1 -eq "auditIfNotExists") -and $effect2 -eq "deployIfNotExists") { if($effect1 -eq "audit") { #loop through audit conditions to find name $auditAllOf = $iDef.Properties.policyRule.if.allOf foreach($condition in $auditAllOf) { if($condition.field -eq "name") { $auditName = $condition.equals break } } } else { $auditName = $iDef.Properties.policyRule.then.details.name } #compare names if($jDef.Properties.policyRule.then.details.name -eq $auditName) { $currentStatus = $true $safeIndexes += $j $safeIndexes += $i } } } for($k = 0; $k -lt $safeIndexes.Count; ++$k) { if($i -eq $safeIndexes[$k]) { $currentStatus = $true break } } if($currentStatus -eq $false) { if($alreadyAdded) { $guestConfigArray[$currGCCount - 1].Errors += ("The policy `"" + $iDef.Properties.displayName + "`" does not have a matching policy in this initiative.") Write-Warning ("The policy `"" + $iDef.Properties.displayName + "`" does not have a matching policy in its initiative, `"" + $def.Properties.displayName + "`".") } else { $guestConfigArray += ([PSCustomObject]@{ Name = $assignment.Properties.displayName Status = $false Warnings = @() Errors = @(("The policy `"" + $iDef.Properties.displayName + "`" does not have a matching policy in this initiative.")) Details = @{ Assignment = $assignment Definition = $def Initiative = $true } }) $alreadyAdded = $true $currGCCount += 1 } Write-Host "" $totalErrors += 1 $success = $false } } } } else { #If it's not in an initiative, add it to the array $guestConfigArray += ([PSCustomObject]@{ Name = $assignment.Properties.displayName Status = $false Warnings = @("This policy is not in an initiative") Errors = @() Details = @{ Assignment = $assignment Definition = $def Initiative = $false } }) $currGCCount += 1 if($def.Properties.policyRule.then.effect -eq "DeployIfNotExists") { $permissionError = CheckAssignmentPermissions -definition $def -assignment $assignment if($permissionError) { $guestConfigArray[$currGCCount - 1].Errors += $permissionError } } } } } } } if($guestConfigCount -eq 0) { Write-Verbose "There are no Guest Configuration Policies assigned to this scope" } else { #Used to make sure we don't use the same policy in 2 different pairs (also lets us be a bit more efficient by skipping already safe policies) $safeIndeces = @() #Matchmaking loop for([int]$i = 0; $i -lt $guestConfigArray.Count - 1; ++$i) { if($safeIndeces.Contains($i) -or $guestConfigArray[$i].Details.Initiative) { continue } #Get audit name if relevant if($guestConfigArray[$i].Details.Definition.Properties.policyRule.then.effect -eq "audit") { foreach($condition in $guestConfigArray[$i].Details.Definition.Properties.policyRule.if.allOf) { if($condition.field -eq "name") { $auditName = $condition.equals } } } elseif($guestConfigArray[$i].Details.Definition.Properties.policyRule.then.effect -eq "auditIfNotExists") { $auditName = $guestConfigArray[$i].Details.Definition.Properties.policyRule.then.details.name } for([int]$j = $i + 1; $j -lt $guestConfigArray.Count; ++$j) { if($safeIndeces.Contains($j) -or $guestConfigArray[$i].Details.Initiative) { continue } if($guestConfigArray[$i].Details.Definition.Properties.policyRule.then.effect -eq "deployIfNotExists") { $detailsName = $guestConfigArray[$i].Details.Definition.Properties.policyRule.then.details.name if($guestConfigArray[$j].Details.Definition.Properties.policyRule.then.effect -eq "audit" -or $guestConfigArray[$j].Details.Definition.Properties.policyRule.then.effect -eq "auditIfNotExists") { if($guestConfigArray[$j].Details.Definition.Properties.policyRule.then.effect -eq "audit") { foreach($condition in $guestConfigArray[$j].Details.Definition.Properties.policyRule.if.allOf) { if($condition.field -eq "name") { $auditName = $condition.equals } } } else { $auditName = $guestConfigArray[$j].Details.Definition.Properties.policyRule.then.details.name } #Check for match if($auditName -eq $detailsName) { #Match found - write warning about how this will work but isn't recommended ##Write-Warning ("The policies `"" + $guestConfigDefArray[$i].Properties.displayName + "`" and `"" + $guestConfigDefArray[$j].Properties.displayName + ##"`" are a matching pair but are not assigned to an initiative. This will work but is not recommended") $guestConfigArray[$i].Warnings += ("This Policy and `"" + $guestConfigArray[$j].Details.Definition.Properties.displayName + "`" are a matching pair but are not assigned to an initiative. This will work but is not recommended") $guestConfigArray[$j].Warnings += ("This Policy and `"" + $guestConfigArray[$i].Details.Definition.Properties.displayName + "`" are a matching pair but are not assigned to an initiative. This will work but is not recommended") Write-Warning ("The policies `"" + $guestConfigArray[$i].Details.Definition.Properties.displayName + "`" and `"" + $guestConfigArray[$j]["Definition"].Properties.displayName + "`" are a matching pair but are not assigned to an initiative. This will work but is not recommended") Write-Host "" #Also remove the 2 matching defs from the array $safeIndeces += $i $safeIndeces += $j break } } } elseif($guestConfigArray[$i].Details.Definition.Properties.policyRule.then.effect -eq "audit" -or $guestConfigArray[$i].Details.Definition.Properties.policyRule.then.effect -eq "auditIfNotExists") { if($guestConfigArray[$j].Details.Definition.Properties.policyRule.then.effect -eq "deployIfNotExists") { $detailsName = $guestConfigArray[$j].Details.Definition.Properties.policyRule.then.details.name #Check for match if($auditName -eq $detailsName) { $guestConfigArray[$i].Warnings += ("This Policy and `"" + $guestConfigArray[$j].Details.Definition.Properties.displayName + "`" are a matching pair but are not assigned to an initiative. This will work but is not recommended") $guestConfigArray[$j].Warnings += ("This Policy and `"" + $guestConfigArray[$i].Details.Definition.Properties.displayName + "`" are a matching pair but are not assigned to an initiative. This will work but is not recommended") Write-Warning ("The policies `"" + $guestConfigArray[$i].Details.Definition.Properties.displayName + "`" and `"" + $guestConfigArray[$j].Details.Definition.Properties.displayName + "`" are a matching pair but are not assigned to an initiative. This will work but is not recommended") Write-Host "" #Also add the 2 matching defs to a non-check list $safeIndeces += $i $safeIndeces += $j break } } } } } #Print warnings for remaining policies for([int]$i = 0; $i -lt $guestConfigArray.Count; ++$i) { $success = $false if($safeIndeces.Contains($i) -or $guestConfigArray[$i].Details.Initiative) { continue } [string]$name = $guestConfigArray[$i].Name Write-Warning ("The policy `"" + $name + "`" is not in an initiative") $totalErrors += 1 if($guestConfigArray[$i].Details.Definition.Properties.policyRule.then.effect -eq "deployIfNotExists") { $guestConfigArray[$i].Warnings += "It is recommended that this DeployIfNotExists Policy be in an initiative with its corresponding Audit Policy" Write-Warning "It is recommended that this DeployIfNotExists Policy be in an initiative with its corresponding Audit Policy" #We should be able to get the corresponding policy's name if it is Built-in if($guestConfigArray[$i].Details.Definition.Properties.policyType -eq "builtIn") { $corrName = "Audit" + $name.Substring(28) $guestConfigArray[$i].Errors += ("The corresponding Audit Policy, `"" + $corrName + "`" is missing") $guestConfigArray[$i].Details["CorrespondingPolicyName"] = $corrName Write-Warning ("The corresponding Audit Policy, `"" + $corrName + "`" is missing") } else { $guestConfigArray[$i].Errors += "The corresponding Audit Policy is missing" Write-Warning "The corresponding Audit Policy is missing" } } elseif($guestConfigArray[$i].Details.Definition.Properties.policyRule.then.effect -eq "audit") { $guestConfigArray[$i].Warnings += "It is recommended that the Audit Policy be in an initiative with its corresponding DeployIfNotExists Policy" Write-Warning "It is recommended that the Audit Policy be in an initiative with its corresponding DeployIfNotExists Policy" if($guestConfigArray[$i].Details.Definition.Properties.policyType -eq "builtIn") { $corrName = "Deploy requirements to audit" + $name.Substring(5) $guestConfigArray[$i].Errors += ("The corresponding DeployIfNotExists Policy, `"" + $corrName + "`" is missing") $guestConfigArray[$i].Details["CorrespondingPolicyName"] = $corrName Write-Warning ("The corresponding DeployIfNotExists Policy, `"" + $corrName + "`" is missing") } else { $guestConfigArray[$i].Errors += "The corresponding DeployIfNotExists Policy is missing" Write-Warning "The corresponding DeployIfNotExists Policy is missing" } } #to separate policies Write-Host "" } } $returnTable["Status"] = $success $returnTable["GuestConfigurationPolicyCount"] = $guestConfigCount $returnTable["Policies"] = $guestConfigArray return $returnTable } <# .SYNOPSIS Tests VM/ResourceGroup/Subscription for things related to Guest Configuration .DESCRIPTION This cmdlet gets data through ARM resource providers about Guest Configuration scenarios, then analyzes it and returns it as a hash table .PARAMETER ResourceGroupName The Resource Group to run tests in .PARAMETER VMName The VM to run tests on .PARAMETER VM VM to check (this will also let the cmdlet automatically get the resource group from the VM) .PARAMETER Force Switch parameter that allows you to skip any required input .EXAMPLE PS> Get-GuestConfigurationPolicyHealth -ResourceGroupName "MyResourceGroup" -VMName "MyVM" .EXAMPLE PS> $myVM = Get-AzVM -ResourceGroupName "MyResourceGroup" -Name "MyVM" Get-GuestConfigurationPolicyHealth -VM $myVM #> function Get-GuestConfigurationPolicyHealth { [CmdletBinding(DefaultParameterSetName = "Default")] param( [parameter(ParameterSetName = "Scoped", Mandatory = $true)][ValidateNotNullOrEmpty()][String]$ResourceGroupName, [parameter(ParameterSetName = "Scoped", Mandatory = $true)][ValidateNotNullOrEmpty()][String]$VMName, [parameter(ParameterSetName = "Default", Mandatory = $true)][ValidateNotNullOrEmpty()]$VM, [parameter(Mandatory=$false)][switch]$Force ) #Check if the resource group exists (MSIEnabled and ExtensionProperties will check the VM) if($ResourceGroupName -and -not(Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue)) { throw "The given Resource Group could not be found" } $masterReturnTable = @{OverallStatus = $true; CheckResults = @(); Messages = @()} #Write-Host "" #Check MSI on the given VM if($VM) { $msi = Get-VmMSIEnabled -VM $VM $ResourceGroupName = $VM.ResourceGroupName } else { $msi = Get-VmMSIEnabled -ResourceGroupName $ResourceGroupName -VMName $VMName } $masterReturnTable.OverallStatus = $masterReturnTable.OverallStatus -and $msi.Status if($msi.Status -eq $false) { $masterReturnTable.Messages += "MSI is not enabled" } $masterReturnTable.CheckResults += $msi #Check Guest Config Extension on the given VM if($VM) { $gce = Get-VmGuestConfigExtensionProperties -VM $VM } else { $gce = Get-VmGuestConfigExtensionProperties -ResourceGroupName $ResourceGroupName -VMName $VMName } $masterReturnTable.OverallStatus = $masterReturnTable.OverallStatus -and $gce.Status if($gce.Status -eq $false) { $masterReturnTable.Messages += "The Guest Configuration Extension on this machine is not healthy" } $masterReturnTable.CheckResults += $gce #Check assignment errors if($ResourceGroupName) { $perrors = Get-GuestConfigPolicyErrors -ResourceGroupName $ResourceGroupName $Force } else { $perrors = Get-GuestConfigPolicyErrors -CurrentSubscription $Force } $masterReturnTable.OverallStatus = $masterReturnTable.OverallStatus -and $perrors.Status if($perrors.Status -eq $false) { $masterReturnTable.Messages += "There are unhealthy policies in this scope" } for($i = 0; $i -lt $perrors.Policies.Count; ++$i) { $masterReturnTable.CheckResults += $perrors.Policies[$i] } if($msi.Status -eq $false -and -not($gce.Details) -and $perrors.GuestConfigurationPolicyCount -eq 0) { $masterReturnTable["Messages"] += "There are no Guest Configuration Policies assigned to this scope, which is likely the reason why MSI is not enabled and the extension is not installed" } return $masterReturnTable } Export-ModuleMember -Function Get-GuestConfigurationPolicyHealth, Get-NonApplicableVMs |