internal/functions/Save-AzOpsManagementGroupChild.ps1
function Save-AzOpsManagementGroupChild { <# .SYNOPSIS Recursively build/change Management Group hierarchy in file system from provided scope. .DESCRIPTION Recursively build/change Management Group hierarchy in file system from provided scope. .PARAMETER Scope Scope to discover - assumes [AzOpsScope] object .PARAMETER StatePath The root path to where the entire state is being built in. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .EXAMPLE > Save-AzOpsManagementGroupChild -Scope (New-AzOpsScope -scope /providers/Microsoft.Management/managementGroups/contoso) Discover Management Group hierarchy from scope .INPUTS AzOpsScope .OUTPUTS Management Group hierarchy in file system #> [CmdletBinding(SupportsShouldProcess = $true)] [OutputType()] param ( [Parameter(Mandatory = $true)] $Scope, [string] $StatePath = (Get-PSFConfigValue -FullName 'AzOps.Core.State') ) process { Write-AzOpsMessage -LogLevel Debug -LogString 'Save-AzOpsManagementGroupChild.Starting' Invoke-PSFProtectedCommand -ActionString 'Save-AzOpsManagementGroupChild.Creating.Scope' -Target $Scope -ScriptBlock { $scopeObject = New-AzOpsScope -Scope $Scope -StatePath $StatePath -ErrorAction SilentlyContinue -Confirm:$false } -EnableException $true -PSCmdlet $PSCmdlet if (-not $scopeObject) { return } # In case -WhatIf is used Write-AzOpsMessage -LogLevel Debug -LogString 'Save-AzOpsManagementGroupChild.Processing' -LogStringValues $scopeObject.Scope # Construct all file paths for scope $scopeStatepath = $scopeObject.StatePath $statepathFileName = [IO.Path]::GetFileName($scopeStatepath) $statepathDirectory = [IO.Path]::GetDirectoryName($scopeStatepath) $statepathScopeDirectory = [IO.Directory]::GetParent($statepathDirectory).ToString() $statepathScopeDirectoryParent = [IO.Directory]::GetParent($statepathScopeDirectory).ToString() Write-AzOpsMessage -LogLevel Debug -LogString 'Save-AzOpsManagementGroupChild.Data.StatePath' -LogStringValues $scopeStatepath Write-AzOpsMessage -LogLevel Debug -LogString 'Save-AzOpsManagementGroupChild.Data.FileName' -LogStringValues $statepathFileName Write-AzOpsMessage -LogLevel Debug -LogString 'Save-AzOpsManagementGroupChild.Data.Directory' -LogStringValues $statepathDirectory Write-AzOpsMessage -LogLevel Debug -LogString 'Save-AzOpsManagementGroupChild.Data.ScopeDirectory' -LogStringValues $statepathScopeDirectory Write-AzOpsMessage -LogLevel Debug -LogString 'Save-AzOpsManagementGroupChild.Data.ScopeDirectoryParent' -LogStringValues $statepathScopeDirectoryParent # If file is found anywhere in "AzOps.Core.State", ensure that it is at the right scope or else it doesn't matter if (Get-ChildItem -Path $Statepath -File -Recurse -Force | Where-Object Name -eq $statepathFileName) { # If the file is found in AzOps State $exisitingScopePath = (Get-ChildItem -Path $Statepath -File -Recurse -Force | Where-Object Name -eq $statepathFileName).Directory # Looking at parent of parent if AutoGeneratedTemplateFolderPath is sub-directory, looking for parent (scope folder) of parent (actual parent in Azure) if ( ((Get-PSFConfigValue -FullName 'AzOps.Core.AutoGeneratedTemplateFolderPath') -notin '.') -and $exisitingScopePath.Parent.Parent.FullName -ne $statepathScopeDirectoryParent) { if ($exisitingScopePath.Parent.FullName -ne $statepathScopeDirectoryParent) { Write-AzOpsMessage -LogLevel Important -LogString 'Save-AzOpsManagementGroupChild.Moving.Source' -LogStringValues $exisitingScopePath Move-Item -Path $exisitingScopePath.Parent -Destination $statepathScopeDirectoryParent -Force Write-AzOpsMessage -LogLevel Important -LogString 'Save-AzOpsManagementGroupChild.Moving.Destination' -LogStringValues $statepathScopeDirectoryParent } } # Files might be at the right scope but not in right AutoGeneratedTemplateFolderPath e.g. when AutoGeneratedTemplateFolderPath is changed. if (-not (Test-Path $statepathDirectory)) { New-Item -Path $statepathDirectory -ItemType Directory -Force | out-null } # For all the files in AutoGeneratedTemplateFolderPath directory, only moving files that are auto generated Get-ChildItem -Path (Get-ChildItem -Path $Statepath -File -Recurse -Force | Where-Object Name -eq $statepathFileName).Directory -File -Filter 'Microsoft.*' | Move-Item -Destination $statepathDirectory -Force } # Create empty object for any discovered subscriptions below $subscriptions = @() # Based on $scopeObject perform unique logic switch ($scopeObject.Type) { managementGroups { ConvertTo-AzOpsState -Resource ($script:AzOpsAzManagementGroup | Where-Object { $_.Name -eq $scopeObject.ManagementGroup }) -ExportPath $scopeObject.StatePath -StatePath $StatePath foreach ($child in $script:AzOpsAzManagementGroup.Where{ $_.Name -eq $scopeObject.ManagementGroup }.Children) { if ($child.Type -eq '/subscriptions') { if ($script:AzOpsSubscriptions.Id -contains $child.Id) { # Subscription discovered at ManagementGroup scope, collect subscription information based on $child object and store information in $subscriptions for later $subscriptions += Save-AzOpsManagementGroupChild -Scope $child.Id -StatePath $StatePath } else { Write-AzOpsMessage -LogLevel Debug -LogString 'Save-AzOpsManagementGroupChild.Subscription.NotFound' -LogStringValues $child.Name } } else { Save-AzOpsManagementGroupChild -Scope $child.Id -StatePath $StatePath } } } subscriptions { if (($script:AzOpsSubscriptions.Id -contains $scopeObject.Scope) -and ($script:AzOpsAzManagementGroup.Children | Where-Object Name -eq $scopeObject.Name)) { # Subscription matching conditions found, construct subscription and object statepath into $return and output information to caller $return = [PSCustomObject]@{ Subscription = ($script:AzOpsAzManagementGroup.Children | Where-Object Name -eq $scopeObject.Name) Path = $scopeObject.StatePath } return $return } } } # If $subscriptions exists process all subscriptions with parallel to increase performance during folder/file creation if ($subscriptions) { # 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 } $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 } & $azOps { ConvertTo-AzOpsState -Resource $subscription.Subscription -ExportPath $subscription.Path -StatePath $runspaceData.StatePath } } Clear-PSFMessage } } } |