internal/functions/Get-AzOpsResourceDefinition.ps1
function Get-AzOpsResourceDefinition { <# .SYNOPSIS This cmdlet discovers resources (Management Groups, Subscriptions, Resource Groups, Resources, Privileged Identity Management resources, Policies, Role Assignments) from the provided input scope. .DESCRIPTION This cmdlet discovers resources (Management Groups, Subscriptions, Resource Groups, Resources, Privileged Identity Management resources, Policies, Role Assignments) from the provided input scope. .PARAMETER Scope Discovery Scope .PARAMETER IncludeResourcesInResourceGroup Discover only resources in these resource groups. .PARAMETER IncludeResourceType Discover only specific resource types. .PARAMETER SkipChildResource Skip childResource discovery. .PARAMETER SkipPim Skip discovery of Privileged Identity Management. .PARAMETER SkipLock Skip discovery of resourceLock. .PARAMETER SkipPolicy Skip discovery of policies. .PARAMETER SkipResource Skip discovery of resources inside resource groups. .PARAMETER SkipResourceGroup Skip discovery of resource groups. .PARAMETER SkipResourceType Skip discovery of specific resource types. .PARAMETER SkipRole Skip discovery of roles for better performance. .PARAMETER StatePath The root folder under which to write the resource json. .EXAMPLE $TenantRootId = '/providers/Microsoft.Management/managementGroups/{0}' -f (Get-AzTenant).Id Get-AzOpsResourceDefinition -scope $TenantRootId -Verbose Discover all resources from root Management Group .EXAMPLE Get-AzOpsResourceDefinition -scope /providers/Microsoft.Management/managementGroups/landingzones -SkipPolicy -SkipResourceGroup Discover all resources from child Management Group, skip discovery of policies and resource groups .EXAMPLE Get-AzOpsResourceDefinition -scope /subscriptions/623625ae-cfb0-4d55-b8ab-0bab99cbf45c Discover all resources from Subscription level .EXAMPLE Get-AzOpsResourceDefinition -scope /subscriptions/623625ae-cfb0-4d55-b8ab-0bab99cbf45c/resourceGroups/myresourcegroup Discover all resources from resource group level .EXAMPLE Get-AzOpsResourceDefinition -scope /subscriptions/623625ae-cfb0-4d55-b8ab-0bab99cbf45c/resourceGroups/contoso-global-dns/providers/Microsoft.Network/privateDnsZones/privatelink.database.windows.net Discover a single resource #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] $Scope, [string[]] $IncludeResourcesInResourceGroup = (Get-PSFConfigValue -FullName 'AzOps.Core.IncludeResourcesInResourceGroup'), [string[]] $IncludeResourceType = (Get-PSFConfigValue -FullName 'AzOps.Core.IncludeResourceType'), [switch] $SkipChildResource = (Get-PSFConfigValue -FullName 'AzOps.Core.SkipChildResource'), [switch] $SkipPim = (Get-PSFConfigValue -FullName 'AzOps.Core.SkipPim'), [switch] $SkipLock = (Get-PSFConfigValue -FullName 'AzOps.Core.SkipLock'), [switch] $SkipPolicy = (Get-PSFConfigValue -FullName 'AzOps.Core.SkipPolicy'), [switch] $SkipResource = (Get-PSFConfigValue -FullName 'AzOps.Core.SkipResource'), [switch] $SkipResourceGroup = (Get-PSFConfigValue -FullName 'AzOps.Core.SkipResourceGroup'), [string[]] $SkipResourceType = (Get-PSFConfigValue -FullName 'AzOps.Core.SkipResourceType'), [switch] $SkipRole = (Get-PSFConfigValue -FullName 'AzOps.Core.SkipRole'), [Parameter(Mandatory = $false)] [string] $StatePath = (Get-PSFConfigValue -FullName 'AzOps.Core.State') ) begin { # Set variables for retry with exponential backoff $backoffMultiplier = 2 $maxRetryCount = 3 # Prepare Input Data for parallel processing $runspaceData = @{ AzOpsPath = "$($script:ModuleRoot)\AzOps.psd1" StatePath = $StatePath runspace_AzOpsAzManagementGroup = $script:AzOpsAzManagementGroup runspace_AzOpsSubscriptions = $script:AzOpsSubscriptions runspace_AzOpsPartialRoot = $script:AzOpsPartialRoot runspace_AzOpsResourceProvider = $script:AzOpsResourceProvider BackoffMultiplier = $backoffMultiplier MaxRetryCount = $maxRetryCount } } process { Write-PSFMessage -Level Important -String 'Get-AzOpsResourceDefinition.Processing' -StringValues $Scope try { $scopeObject = New-AzOpsScope -Scope $Scope -StatePath $StatePath -ErrorAction Stop # Logging output metadata $msgCommon = @{ FunctionName = 'Get-AzOpsResourceDefinition' Target = $ScopeObject } } catch { Write-PSFMessage -Level Warning -String 'Get-AzOpsResourceDefinition.Processing.NotFound' -StringValues $Scope return } if ($scopeObject.Type -notin 'subscriptions', 'managementGroups') { Write-PSFMessage -Level Verbose -String 'Get-AzOpsResourceDefinition.Finished' -StringValues $scopeObject.Scope return } switch ($scopeObject.Type) { subscriptions { Write-PSFMessage -Level Important @msgCommon -String 'Get-AzOpsResourceDefinition.Subscription.Processing' -StringValues $ScopeObject.SubscriptionDisplayName, $ScopeObject.Subscription $subscriptions = Get-AzSubscription -SubscriptionId $scopeObject.Name | Where-Object { "/subscriptions/$($_.Id)" -in $script:AzOpsSubscriptions.id } } managementGroups { Write-PSFMessage -Level Important @msgCommon -String 'Get-AzOpsResourceDefinition.ManagementGroup.Processing' -StringValues $ScopeObject.ManagementGroupDisplayName, $ScopeObject.ManagementGroup $query = "resourcecontainers | where type == 'microsoft.management/managementgroups' | order by ['id'] asc" $managementgroups = Search-AzOpsAzGraph -ManagementGroupName $scopeObject.Name -Query $query -ErrorAction Stop | Where-Object { $_.id -in $script:AzOpsAzManagementGroup.Id } $subscriptions = Get-AzOpsNestedSubscription -Scope $scopeObject.Name if ($managementgroups) { # Process managementGroup scope in parallel $managementgroups | Foreach-Object -ThrottleLimit (Get-PSFConfigValue -FullName 'AzOps.Core.ThrottleLimit') -Parallel { $managementgroup = $_ $runspaceData = $using:runspaceData Import-Module "$([PSFramework.PSFCore.PSFCoreHost]::ModuleRoot)/PSFramework.psd1" $azOps = Import-Module $runspaceData.AzOpsPath -Force -PassThru & $azOps { $script:AzOpsAzManagementGroup = $runspaceData.runspace_AzOpsAzManagementGroup $script:AzOpsSubscriptions = $runspaceData.runspace_AzOpsSubscriptions $script:AzOpsPartialRoot = $runspaceData.runspace_AzOpsPartialRoot $script:AzOpsResourceProvider = $runspaceData.runspace_AzOpsResourceProvider } # Process Privileged Identity Management resources, Policies and Roles at managementGroup scope if ((-not $using:SkipPim) -or (-not $using:SkipPolicy) -or (-not $using:SkipRole)) { & $azOps { $ScopeObject = New-AzOpsScope -Scope $managementgroup.id -StatePath $runspaceData.Statepath -ErrorAction Stop if (-not $using:SkipPim) { Get-AzOpsPim -ScopeObject $ScopeObject -StatePath $runspaceData.Statepath } if (-not $using:SkipPolicy) { $policyExemptions = Get-AzOpsPolicyExemption -ScopeObject $ScopeObject if ($policyExemptions) { $policyExemptions | ConvertTo-AzOpsState -StatePath $runspaceData.Statepath } } if (-not $using:SkipRole) { Get-AzOpsRole -ScopeObject $ScopeObject -StatePath $runspaceData.Statepath } } } } } } } #region Process Policies at $scopeObject if (-not $SkipPolicy) { Get-AzOpsPolicy -ScopeObject $scopeObject -Subscription $subscriptions -StatePath $StatePath } #endregion Process Policies at $scopeObject #region Process subscription scope in parallel if ($subscriptions) { $subscriptions | Foreach-Object -ThrottleLimit (Get-PSFConfigValue -FullName 'AzOps.Core.ThrottleLimit') -Parallel { $subscription = $_ $runspaceData = $using:runspaceData Import-Module "$([PSFramework.PSFCore.PSFCoreHost]::ModuleRoot)/PSFramework.psd1" $azOps = Import-Module $runspaceData.AzOpsPath -Force -PassThru & $azOps { $script:AzOpsAzManagementGroup = $runspaceData.runspace_AzOpsAzManagementGroup $script:AzOpsSubscriptions = $runspaceData.runspace_AzOpsSubscriptions $script:AzOpsPartialRoot = $runspaceData.runspace_AzOpsPartialRoot $script:AzOpsResourceProvider = $runspaceData.runspace_AzOpsResourceProvider } # Process Privileged Identity Management resources, Policies, Locks and Roles at subscription scope if ((-not $using:SkipPim) -or (-not $using:SkipPolicy) -or (-not $using:SkipLock) -or (-not $using:SkipRole)) { & $azOps { $scopeObject = New-AzOpsScope -Scope ($subscription.Type + '/' + $subscription.Id) -StatePath $runspaceData.Statepath -ErrorAction Stop if (-not $using:SkipPim) { Get-AzOpsPim -ScopeObject $scopeObject -StatePath $runspaceData.Statepath } if (-not $using:SkipPolicy) { $policyExemptions = Get-AzOpsPolicyExemption -ScopeObject $scopeObject if ($policyExemptions) { $policyExemptions | ConvertTo-AzOpsState -StatePath $runspaceData.Statepath } } if (-not $using:SkipLock) { Get-AzOpsResourceLock -ScopeObject $scopeObject -StatePath $runspaceData.Statepath } if (-not $using:SkipRole) { Get-AzOpsRole -ScopeObject $scopeObject -StatePath $runspaceData.Statepath } } } } } #endregion Process subscription scope in parallel #region Process Resource Groups if ($SkipResourceGroup -or (-not $subscriptions)) { if ($SkipResourceGroup) { Write-PSFMessage -Level Verbose @msgCommon -String 'Get-AzOpsResourceDefinition.SkippingResourceGroup' } else { Write-PSFMessage -Level Verbose @msgCommon -String 'Get-AzOpsResourceDefinition.Subscription.NotFound' } } else { if ((Get-PSFConfigValue -FullName 'AzOps.Core.SubscriptionsToIncludeResourceGroups') -ne '*') { $subscriptionsToIncludeResourceGroups = $subscriptions | Where-Object { $_.Id -in (Get-PSFConfigValue -FullName 'AzOps.Core.SubscriptionsToIncludeResourceGroups') } } $query = "resourcecontainers | where type == 'microsoft.resources/subscriptions/resourcegroups' | where managedBy == '' | order by ['id'] asc" if ($subscriptionsToIncludeResourceGroups) { $resourceGroups = Search-AzOpsAzGraph -Subscription $subscriptionsToIncludeResourceGroups -Query $query -ErrorAction Stop } else { $resourceGroups = Search-AzOpsAzGraph -Subscription $subscriptions -Query $query -ErrorAction Stop } if ($resourceGroups) { # Process Resource Groups in parallel $resourceGroups | Foreach-Object -ThrottleLimit (Get-PSFConfigValue -FullName 'AzOps.Core.ThrottleLimit') -Parallel { $resourceGroup = $_ $runspaceData = $using:runspaceData Import-Module "$([PSFramework.PSFCore.PSFCoreHost]::ModuleRoot)/PSFramework.psd1" $azOps = Import-Module $runspaceData.AzOpsPath -Force -PassThru & $azOps { $script:AzOpsAzManagementGroup = $runspaceData.runspace_AzOpsAzManagementGroup $script:AzOpsSubscriptions = $runspaceData.runspace_AzOpsSubscriptions $script:AzOpsPartialRoot = $runspaceData.runspace_AzOpsPartialRoot $script:AzOpsResourceProvider = $runspaceData.runspace_AzOpsResourceProvider } # Create Resource Group in file system & $azOps { ConvertTo-AzOpsState -Resource $resourceGroup -StatePath $runspaceData.Statepath } # Process Privileged Identity Management resources, Policies, Locks and Roles at resource group scope if ((-not $using:SkipPim) -or (-not $using:SkipPolicy) -or (-not $using:SkipRole) -or (-not $using:SkipLock)) { & $azOps { $rgScopeObject = New-AzOpsScope -Scope $resourceGroup.id -StatePath $runspaceData.Statepath -ErrorAction Stop if (-not $using:SkipLock) { Get-AzOpsResourceLock -ScopeObject $rgScopeObject -StatePath $runspaceData.Statepath } if (-not $using:SkipPim) { Get-AzOpsPim -ScopeObject $rgScopeObject -StatePath $runspaceData.Statepath } if (-not $using:SkipPolicy) { $policyExemptions = Get-AzOpsPolicyExemption -ScopeObject $rgScopeObject if ($policyExemptions) { $policyExemptions | ConvertTo-AzOpsState -StatePath $runspaceData.Statepath } } if (-not $using:SkipRole) { Get-AzOpsRole -ScopeObject $rgScopeObject -StatePath $runspaceData.Statepath } } } } } else { Write-PSFMessage -Level Verbose @msgCommon -String 'Get-AzOpsResourceDefinition.NoResourceGroup' -StringValues $scopeObject.Name } # Process Policies at Resource Group scope if (-not $SkipPolicy) { if ($subscriptionsToIncludeResourceGroups) { Get-AzOpsPolicy -ScopeObject $scopeObject -Subscription $subscriptions -SubscriptionsToIncludeResourceGroups $subscriptionsToIncludeResourceGroups -ResourceGroup -StatePath $StatePath } else { Get-AzOpsPolicy -ScopeObject $scopeObject -Subscription $subscriptions -ResourceGroup -StatePath $StatePath } } # Process Resources at Resource Group scope if (-not $SkipResource) { Write-PSFMessage -Level Verbose @msgCommon -String 'Get-AzOpsResourceDefinition.Processing.Resource.Discovery' -StringValues $scopeObject.Name try { $SkipResourceType | ForEach-Object { $skipResourceTypes += ($(if($skipResourceTypes){","}) + "'" + $_ + "'") } $query = "resources | where type !in~ ($skipResourceTypes)" if ($IncludeResourceType -ne "*") { $IncludeResourceType | ForEach-Object { $includeResourceTypes += ($(if($includeResourceTypes){","}) + "'" + $_ + "'") } $query = $query + " and type in~ ($includeResourceTypes)" } if ($IncludeResourcesInResourceGroup -ne "*") { $IncludeResourcesInResourceGroup | ForEach-Object { $includeResourcesInResourceGroups += ($(if($includeResourcesInResourceGroups){","}) + "'" + $_ + "'") } $query = $query + " and resourceGroup in~ ($includeResourcesInResourceGroups)" } $query = $query + " | order by ['id'] asc" $resourcesBase = Search-AzOpsAzGraph -Subscription $subscriptions -Query $query -ErrorAction Stop } catch { Write-PSFMessage -Level Warning @msgCommon -String 'Get-AzOpsResourceDefinition.Processing.Resource.Warning' -StringValues $scopeObject.Name } if ($resourcesBase) { $resources = @() foreach ($resource in $resourcesBase) { if ($resourceGroups | Where-Object { $_.name -eq $resource.resourceGroup -and $_.subscriptionId -eq $resource.subscriptionId }) { Write-PSFMessage -Level Verbose @msgCommon -String 'Get-AzOpsResourceDefinition.Processing.Resource' -StringValues $resource.name, $resource.resourcegroup -Target $resource $resources += $resource ConvertTo-AzOpsState -Resource $resource -StatePath $Statepath } } } else { Write-PSFMessage -Level Verbose @msgCommon -String 'Get-AzOpsResourceDefinition.Processing.Resource.Discovery.NotFound' -StringValues $scopeObject.Name } } else { Write-PSFMessage -Level Verbose @msgCommon -String 'Get-AzOpsResourceDefinition.SkippingResources' } # Process resources as scope in parallel, look for childResource if (-not $SkipResource -and -not $SkipChildResource) { $resources | Foreach-Object -ThrottleLimit (Get-PSFConfigValue -FullName 'AzOps.Core.ThrottleLimit') -Parallel { $resource = $_ $runspaceData = $using:runspaceData Import-Module "$([PSFramework.PSFCore.PSFCoreHost]::ModuleRoot)/PSFramework.psd1" $azOps = Import-Module $runspaceData.AzOpsPath -Force -PassThru & $azOps { $script:AzOpsAzManagementGroup = $runspaceData.runspace_AzOpsAzManagementGroup $script:AzOpsSubscriptions = $runspaceData.runspace_AzOpsSubscriptions $script:AzOpsPartialRoot = $runspaceData.runspace_AzOpsPartialRoot $script:AzOpsResourceProvider = $runspaceData.runspace_AzOpsResourceProvider } $context = Get-AzContext $context.Subscription.Id = $resource.subscriptionId $tempExportPath = [System.IO.Path]::GetTempPath() + (New-Guid).ToString() + '.json' try { & $azOps { $exportParameters = @{ Resource = $resource.id ResourceGroupName = $resource.resourceGroup SkipAllParameterization = $true Path = $tempExportPath DefaultProfile = $context | Select-Object -First 1 } Invoke-AzOpsScriptBlock -ArgumentList $exportParameters -ScriptBlock { param ( $ExportParameters ) $param = $ExportParameters | Write-Output Export-AzResourceGroup @param -Confirm:$false -Force -ErrorAction Stop | Out-Null } -RetryCount $runspaceData.MaxRetryCount -RetryWait $runspaceData.BackoffMultiplier -RetryType Exponential } $exportResources = (Get-Content -Path $tempExportPath | ConvertFrom-Json).resources $resourceGroup = $using:resourceGroups | Where-Object {$_.subscriptionId -eq $resource.subscriptionId -and $_.name -eq $resource.resourceGroup} foreach ($exportResource in $exportResources) { if (-not(($resource.name -eq $exportResource.name) -and ($resource.type -eq $exportResource.type))) { Write-PSFMessage -Level Verbose -String 'Get-AzOpsResourceDefinition.Processing.ChildResource' -StringValues $exportResource.name, $resource.resourceGroup -Target $exportResource $ChildResource = @{ resourceProvider = $exportResource.type -replace '/', '_' resourceName = $exportResource.name -replace '/', '_' parentResourceId = $resourceGroup.id } if (Get-Member -InputObject $exportResource -name 'dependsOn') { $exportResource.PsObject.Members.Remove('dependsOn') } $resourceHash = @{resources = @($exportResource) } & $azOps { ConvertTo-AzOpsState -Resource $resourceHash -ChildResource $ChildResource -StatePath $runspaceData.Statepath } } } } catch { Write-PSFMessage -Level Warning -String 'Get-AzOpsResourceDefinition.ChildResource.Warning' -StringValues $resource.resourceGroup, $_ } if (Test-Path -Path $tempExportPath) { Remove-Item -Path $tempExportPath } } } else { Write-PSFMessage -Level Verbose @msgCommon -String 'Get-AzOpsResourceDefinition.SkippingChildResources' } } #endregion Process Resource Groups Write-PSFMessage -Level Verbose @msgCommon -String 'Get-AzOpsResourceDefinition.Finished' -StringValues $scopeObject.Scope } } |