internal/functions/Save-AzOpsManagementGroupChildren.ps1

function Save-AzOpsManagementGroupChildren {

    <#
        .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-AzOpsManagementGroupChildren -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-PSFMessage -Level Debug -String 'Save-AzOpsManagementGroupChildren.Starting'
        Invoke-PSFProtectedCommand -ActionString 'Save-AzOpsManagementGroupChildren.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-PSFMessage -Level Important -String 'Save-AzOpsManagementGroupChildren.Processing' -StringValues $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-PSFMessage -Level Debug -String 'Save-AzOpsManagementGroupChildren.Data.StatePath' -StringValues $scopeStatepath
        Write-PSFMessage -Level Debug -String 'Save-AzOpsManagementGroupChildren.Data.FileName' -StringValues $statepathFileName
        Write-PSFMessage -Level Debug -String 'Save-AzOpsManagementGroupChildren.Data.Directory' -StringValues $statepathDirectory
        Write-PSFMessage -Level Debug -String 'Save-AzOpsManagementGroupChildren.Data.ScopeDirectory' -StringValues $statepathScopeDirectory
        Write-PSFMessage -Level Debug -String 'Save-AzOpsManagementGroupChildren.Data.ScopeDirectoryParent' -StringValues $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-PSFMessage -Level Verbose -String 'Save-AzOpsManagementGroupChildren.Moving.Source' -StringValues $exisitingScopePath
                    Move-Item -Path $exisitingScopePath.Parent -Destination $statepathScopeDirectoryParent -Force
                    Write-PSFMessage -Level Important -String 'Save-AzOpsManagementGroupChildren.Moving.Destination' -StringValues $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

        }
        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) {
                            Save-AzOpsManagementGroupChildren -Scope $child.Id -StatePath $StatePath
                        }
                        else {
                            Write-PSFMessage -Level Warning -String 'Save-AzOpsManagementGroupChildren.Subscription.NotFound' -StringValues $child.Name
                        }
                    }
                    else {
                        Save-AzOpsManagementGroupChildren -Scope $child.Id -StatePath $StatePath
                    }
                }
            }
            subscriptions {
                ConvertTo-AzOpsState -Resource ($script:AzOpsAzManagementGroup.children | Where-Object Name -eq $scopeObject.name) -ExportPath $scopeObject.statepath -StatePath $StatePath
            }
        }
    }

}