AzPolicyLens.Discovery.psm1
|
using module ./AzPolicyLens.Discovery.helper.psm1 using module ./AzPolicyLens.Discovery.type.psm1 #function to create AES encryption key and IV for encrypting the environment discovery file Function New-AzplEncryptionKey { [CmdletBinding(SupportsShouldProcess)] [OutputType([System.Object])] param ( [parameter(Mandatory = $true, HelpMessage = 'The directory path for the encryption key.')] [ValidateScript({ Test-Path -path $_ -PathType 'Container' })] [string]$OutputDir, [parameter(Mandatory = $false, HelpMessage = 'The file name for the encryption key.')] [ValidateNotNullOrEmpty()] [string]$FileName = 'AzplEncryptionKey.json' ) #Create Key File $KeyFilePath = Join-Path -Path $OutputDir -ChildPath $FileName if ($PSCmdlet.ShouldProcess($KeyFilePath)) { $key = newAesKey -KeySize 256 -OutputFilePath $KeyFilePath } $key } #function to get all relevant policy resources and management group hierarchy based on a management group scope Function Invoke-AzplEnvironmentDiscovery { [CmdletBinding()] [OutputType([String])] param ( [parameter(Mandatory = $true, ParameterSetName = 'EncryptionViaKeyFile', HelpMessage = 'The Top-level Management Group name.')] [parameter(Mandatory = $true, ParameterSetName = 'EncryptionViaKeyText', HelpMessage = 'The Top-level Management Group name.')] [parameter(Mandatory = $true, ParameterSetName = 'NoEncryption', HelpMessage = 'The Top-level Management Group name.')] [ValidateNotNullOrEmpty()] [string]$TopLevelManagementGroupName, [parameter(Mandatory = $true, ParameterSetName = 'EncryptionViaKeyFile', HelpMessage = "The Azure OAuth token for accessing 'https://management.azure.com/'. This is required to invoke the Azure Resource Graph query via it's REST API.")] [parameter(Mandatory = $true, ParameterSetName = 'EncryptionViaKeyText', HelpMessage = "The Azure OAuth token for accessing 'https://management.azure.com/'. This is required to invoke the Azure Resource Graph query via it's REST API.")] [parameter(Mandatory = $true, ParameterSetName = 'NoEncryption', HelpMessage = "The Azure OAuth token for accessing 'https://management.azure.com/'. This is required to invoke the Azure Resource Graph query via it's REST API.")] [ValidateNotNullOrEmpty()] [string]$Token, [parameter(Mandatory = $true, ParameterSetName = 'EncryptionViaKeyFile', HelpMessage = 'Additional builtin Policy Metadata from security control frameworks to include.')] [parameter(Mandatory = $true, ParameterSetName = 'EncryptionViaKeyText', HelpMessage = 'Additional builtin Policy Metadata from security control frameworks to include.')] [parameter(Mandatory = $true, ParameterSetName = 'NoEncryption', HelpMessage = 'Additional builtin Policy Metadata from security control frameworks to include.')] [BuiltInSecurityControlConfig[]]$AdditionalBuiltInPolicyMetadataConfig, [parameter(Mandatory = $true, ParameterSetName = 'EncryptionViaKeyFile', HelpMessage = 'Export file directory')] [parameter(Mandatory = $true, ParameterSetName = 'EncryptionViaKeyText', HelpMessage = 'Export file directory')] [parameter(Mandatory = $true, ParameterSetName = 'NoEncryption', HelpMessage = 'Export file directory')] [ValidateScript({ Test-Path -Path $_ -PathType 'Container' })] [string]$OutputFileDirectory, [parameter(Mandatory = $false, ParameterSetName = 'EncryptionViaKeyFile', HelpMessage = 'Export file base name (without file extension)')] [parameter(Mandatory = $false, ParameterSetName = 'EncryptionViaKeyText', HelpMessage = 'Export file base name (without file extension)')] [parameter(Mandatory = $false, ParameterSetName = 'NoEncryption', HelpMessage = 'Export file base name (without file extension)')] [ValidateNotNullOrEmpty()] [string]$OutputFileBaseName = $TopLevelManagementGroupName, [parameter(Mandatory = $true, ParameterSetName = 'EncryptionViaKeyFile', HelpMessage = 'AES Encryption key file path. If specified, the output file will be encrypted using this key.')] [ValidateScript({ Test-Path -Path $_ -PathType 'Leaf' })] [string]$EncryptionKeyFilePath, [parameter(Mandatory = $true, ParameterSetName = 'EncryptionViaKeyText', HelpMessage = 'AES Encryption Key Content.')] [ValidateNotNullOrEmpty()] [string]$EncryptionKey, [parameter(Mandatory = $true, ParameterSetName = 'EncryptionViaKeyText', HelpMessage = 'AES Encryption Initialization Vector (IV) Content.')] [ValidateNotNullOrEmpty()] [string]$EncryptionIV ) #Define file names $hierarchyFilePath = Join-Path -Path $OutputFileDirectory -ChildPath 'managementGroupHierarchy.json' $policyAssignmentFilePath = Join-Path -Path $OutputFileDirectory -ChildPath 'policyAssignments.json' $policyInitiativeFilePath = Join-Path -Path $OutputFileDirectory -ChildPath 'policyInitiatives.json' $policyDefinitionFilePath = Join-Path -Path $OutputFileDirectory -ChildPath 'policyDefinitions.json' $policyExemptionFilePath = Join-Path -Path $OutputFileDirectory -ChildPath 'policyExemptions.json' $policyMetadataFilePath = Join-Path -Path $OutputFileDirectory -ChildPath 'policyMetadata.json' $complianceDataFilePath = Join-Path -Path $OutputFileDirectory -ChildPath 'complianceData.json' #Get policy resources $utcNow = (Get-Date).ToUniversalTime() $allPolicyResourcesParams = @{ ManagementGroupName = $TopLevelManagementGroupName Token = $Token } if ($PSBoundParameters.ContainsKey('AdditionalBuiltInPolicyMetadataConfig')) { $allPolicyResourcesParams.add('additionalBuiltInPolicyMetadataConfig', $AdditionalBuiltInPolicyMetadataConfig) } Write-Verbose "[$(getCurrentUTCString)]: Retrieving all relevant policy resources under the management group '$TopLevelManagementGroupName'." -verbose $PolicyResources = getAllPolicyResources @allPolicyResourcesParams Write-Verbose "[$(getCurrentUTCString)]: Found $($PolicyResources.assignments.Count) policy assignments in the management group hierarchy." -verbose Write-Verbose "[$(getCurrentUTCString)]: Found $($PolicyResources.initiatives.Count) policy initiatives in the management group hierarchy." -verbose Write-Verbose "[$(getCurrentUTCString)]: Found $($PolicyResources.definitions.Count) policy definitions in the management group hierarchy." -verbose Write-Verbose "[$(getCurrentUTCString)]: Found $($PolicyResources.exemptions.Count) policy exemptions in the management group hierarchy." -verbose Write-Verbose "[$(getCurrentUTCString)]: Found $($PolicyResources.policyMetadata.Count) built-in in-used policy metadata in the management group hierarchy." -verbose Write-Verbose "[$(getCurrentUTCString)]: Found $($PolicyResources.builtInDefinitionInUnAssignedCustomInitiative.Count) built-in policy definitions used in unassigned custom policy initiatives in the management group hierarchy." -verbose #get all managed identities for the policy assignments Write-Verbose "[$(getCurrentUTCString)]: Retrieving managed identities used in the policy assignments." $assignmentManagedIdentities = getPolicyAssignmentManagedIdentityPrincipalIds -assignments $PolicyResources.assignments #get all principal Ids for all the policy initiatives $assignmentManagedIdentityPrincipalIds = @() foreach ($item in $assignmentManagedIdentities) { #add system assigned managed identity principal Id if ($item.systemAssignedPrincipalId) { $assignmentManagedIdentityPrincipalIds += $item.systemAssignedPrincipalId } #add user assigned managed identity principal Id if ($item.userAssignedIdentities) { foreach ($userAssignedIdentity in $item.userAssignedIdentities) { if ($userAssignedIdentity.principalId) { $assignmentManagedIdentityPrincipalIds += $userAssignedIdentity.principalId } } } } $assignmentManagedIdentityPrincipalIds = $assignmentManagedIdentityPrincipalIds | Sort-Object -Unique if ($assignmentManagedIdentityPrincipalIds.Count -gt 0) { Write-Verbose "[$(getCurrentUTCString)]: Found $($assignmentManagedIdentityPrincipalIds.Count) unique managed identity principal Ids used in the policy assignments." -Verbose #Get role assignments for the managed identities $roleAssignments = getRoleAssignments -principalIds $assignmentManagedIdentityPrincipalIds -token $Token if ($roleAssignments.Count -gt 0) { Write-Verbose "[$(getCurrentUTCString)]: Found $($roleAssignments.Count) role assignments for the managed identities used in the policy assignments." -Verbose #get Role definitions used in the role assignments $roleDefinitionIds = $roleAssignments.roleDefinitionId | Sort-Object -Unique Write-Verbose "[$(getCurrentUTCString)]: Found $($roleDefinitionIds.Count) unique role definition IDs used in the role assignments. Getting details of these Role Definitions.." $roleDefinitions = getRoleDefinitions -resourceIds $roleDefinitionIds -token $Token if ($roleDefinitions.Count -eq 0) { Write-Warning "[$(getCurrentUTCString)]: No role definitions found for the role assignments. Make sure you have required permission to perform this Azure resource graph search on the tenant scope." } else { Write-Verbose "[$(getCurrentUTCString)]: Found $($roleDefinitions.Count) role definitions used in the role assignments." -Verbose } } else { Write-Warning "[$(getCurrentUTCString)]: No role assignments found for the managed identities used in the policy assignments. Make sure you have required permission to perform this Azure resource graph search on the tenant scope." } } else { Write-Warning "[$(getCurrentUTCString)]: No managed identities found in the policy assignments." } #Get management group hierarchy $managementGroups = getManagementGroupHierarchy -ManagementGroupName $TopLevelManagementGroupName -token $Token if ($managementGroups.Count -eq 0) { Write-Warning "[$(getCurrentUTCString)]: No management group hierarchy found for '$TopLevelManagementGroupName'" } else { Write-Verbose "[$(getCurrentUTCString)]: Found $($managementGroups.Count) management groups in the hierarchy." } #Get subscriptions under the top-level management group $subscriptions = getSubscriptionsUnderManagementGroup -ManagementGroupName $TopLevelManagementGroupName -token $Token if ($subscriptions.Count -eq 0) { Write-Warning "[$(getCurrentUTCString)]: No subscriptions found for '$TopLevelManagementGroupName'" } else { Write-Verbose "[$(getCurrentUTCString)]: Found $($subscriptions.Count) subscriptions in the hierarchy." } #Create ordered hashtables for output $hierarchyDetails = [ordered]@{ timeStamp = $utcNow.tostring('yyyy-MM-ddTHH:mmZ') topLevelManagementGroupName = $TopLevelManagementGroupName managementGroups = $managementGroups subscriptions = $subscriptions } $policyAssignmentDetails = [ordered]@{ assignments = $PolicyResources.assignments roleAssignments = $roleAssignments roleDefinitions = $roleDefinitions } $policyInitiativeDetails = [ordered]@{ initiatives = $PolicyResources.initiatives } $policyDefinitionDetails = [ordered]@{ definitions = $PolicyResources.definitions builtInDefinitionInUnAssignedCustomInitiative = $PolicyResources.builtInDefinitionInUnAssignedCustomInitiative } $policyExemptionDetails = [ordered]@{ exemptions = $PolicyResources.exemptions } $policyMetadataDetails = [ordered]@{ policyMetadata = $PolicyResources.policyMetadata additionalBuiltInPolicyMetadataConfig = $AdditionalBuiltInPolicyMetadataConfig } $complianceDataDetails = [ordered]@{ assignmentCompliance = $PolicyResources.assignmentCompliance subscriptionComplianceSummary = $PolicyResources.subscriptionComplianceSummary complianceSummaryByPolicyDefinitionGroup = $PolicyResources.complianceSummaryByPolicyDefinitionGroup } #Encrypt files if required if ($($PsCmdlet.ParameterSetName) -eq 'EncryptionViaKeyFile') { Write-Verbose "[$(getCurrentUTCString)]: Encrypting the environment details using the encryption key file '$EncryptionKeyFilePath'." -Verbose $hierarchyOutput = encryptStuff -InputText ($hierarchyDetails | ConvertTo-Json -depth 100) -KeyFilePath $EncryptionKeyFilePath $assignmentOutput = encryptStuff -InputText ($policyAssignmentDetails | ConvertTo-Json -depth 100) -KeyFilePath $EncryptionKeyFilePath $initiativeOutput = encryptStuff -InputText ($policyInitiativeDetails | ConvertTo-Json -depth 100) -KeyFilePath $EncryptionKeyFilePath $definitionOutput = encryptStuff -InputText ($policyDefinitionDetails | ConvertTo-Json -depth 100) -KeyFilePath $EncryptionKeyFilePath $exemptionOutput = encryptStuff -InputText ($policyExemptionDetails | ConvertTo-Json -depth 100) -KeyFilePath $EncryptionKeyFilePath $metadataOutput = encryptStuff -InputText ($policyMetadataDetails | ConvertTo-Json -depth 100) -KeyFilePath $EncryptionKeyFilePath $complianceOutput = encryptStuff -InputText ($complianceDataDetails | ConvertTo-Json -depth 100) -KeyFilePath $EncryptionKeyFilePath } elseif ($($PsCmdlet.ParameterSetName) -eq 'EncryptionViaKeyText') { Write-Verbose "[$(getCurrentUTCString)]: Encrypting the environment details using the encryption key and IV." -Verbose $hierarchyOutput = encryptStuff -InputText ($hierarchyDetails | ConvertTo-Json -depth 100) -AESKey $EncryptionKey -AESIV $EncryptionIV $assignmentOutput = encryptStuff -InputText ($policyAssignmentDetails | ConvertTo-Json -depth 100) -AESKey $EncryptionKey -AESIV $EncryptionIV $initiativeOutput = encryptStuff -InputText ($policyInitiativeDetails | ConvertTo-Json -depth 100) -AESKey $EncryptionKey -AESIV $EncryptionIV $definitionOutput = encryptStuff -InputText ($policyDefinitionDetails | ConvertTo-Json -depth 100) -AESKey $EncryptionKey -AESIV $EncryptionIV $exemptionOutput = encryptStuff -InputText ($policyExemptionDetails | ConvertTo-Json -depth 100) -AESKey $EncryptionKey -AESIV $EncryptionIV $metadataOutput = encryptStuff -InputText ($policyMetadataDetails | ConvertTo-Json -depth 100) -AESKey $EncryptionKey -AESIV $EncryptionIV $complianceOutput = encryptStuff -InputText ($complianceDataDetails | ConvertTo-Json -depth 100) -AESKey $EncryptionKey -AESIV $EncryptionIV } else { Write-Verbose "[$(getCurrentUTCString)]: Exporting the environment details as plain text." -Verbose $hierarchyOutput = $hierarchyDetails | ConvertTo-Json -depth 100 $assignmentOutput = $policyAssignmentDetails | ConvertTo-Json -depth 100 $initiativeOutput = $policyInitiativeDetails | ConvertTo-Json -depth 100 $definitionOutput = $policyDefinitionDetails | ConvertTo-Json -depth 100 $exemptionOutput = $policyExemptionDetails | ConvertTo-Json -depth 100 $metadataOutput = $policyMetadataDetails | ConvertTo-Json -depth 100 $complianceOutput = $complianceDataDetails | ConvertTo-Json -depth 100 } #Create output files $hierarchyOutput | Out-File $hierarchyFilePath $assignmentOutput | Out-File $policyAssignmentFilePath $initiativeOutput | Out-File $policyInitiativeFilePath $definitionOutput | Out-File $policyDefinitionFilePath $exemptionOutput | Out-File $policyExemptionFilePath $metadataOutput | Out-File $policyMetadataFilePath $complianceOutput | Out-File $complianceDataFilePath #Zip the output files $compressedFilePath = Join-Path $OutputFileDirectory -ChildPath ($OutputFileBaseName + '.zip') $includedFiles = @( $hierarchyFilePath $policyAssignmentFilePath $policyInitiativeFilePath $policyDefinitionFilePath $policyExemptionFilePath $policyMetadataFilePath $complianceDataFilePath ) Write-Verbose "[$(getCurrentUTCString)]: Compressing the output files to '$compressedFilePath'." -Verbose Compress-Archive -Path $includedFiles -DestinationPath $compressedFilePath -Force #Delete the uncompressed file Foreach ($file in $includedFiles) { Remove-Item -Path $file -Force } $compressedFilePath } |