scripts/private/EnumerateStructure.ps1

function EnumerateStructure {
    [CmdletBinding()]
    [OutputType([System.Collections.Generic.List[AzBuilderScope]])]

    Param (
        [Parameter(Mandatory, Position = 0)]
        [string] $Path
    )

    try {
        [regex] $SubscriptionRegex = [regex]::new('(?i)[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}')
        [regex] $LocationRegex = [regex]::new('(?i)(.+)(?:\()([a-zA-Z]+)(?:\))')

        [System.IO.DirectoryInfo] $Root = Get-Item -Path $Path
        [System.Collections.Generic.List[AzBuilderScope]] $AzBuilderList = @()
        [System.IO.FileInfo[]] $Templates = $Root.GetFiles('*.json')

        if ($Templates) {
            [AzBuilderScope] $RootScope = [AzBuilderScope]::new('Tenant Root Group', 'Tenant', $Root.FullName)
            $RootScope.Templates = GetTemplates $Templates
        }

        foreach ($RootItem in ($Root.GetDirectories() | Where-Object -FilterScript {$_.Name -ne '.deployments' -or $_.Name -ne '.state'})) {
            if ($RootItem.BaseName -match $SubscriptionRegex) {
                [string] $Scope = 'Subscription'
                [string] $RootItemName = $SubscriptionRegex.Match($RootItem.BaseName).Groups[0].Value
            } else {
                [string] $Scope = 'ManagementGroup'
                [string] $RootItemName = $RootItem.Name
            }

            [AzBuilderScope] $RootAzBuilderScope = [AzBuilderScope]::new($RootItemName, $Scope, ('{0}' -f $RootItem.FullName.ToLower().Replace(('{0}{1}' -f $Path.ToLower(), [System.IO.Path]::DirectorySeparatorChar), '')))
            $AzBuilderList.Add($RootAzBuilderScope)

            foreach ($ChildPath in $RootAzBuilderScope.Path) {
                [System.IO.DirectoryInfo[]] $Children = Get-ChildItem -Path ('{0}\{1}' -f $Path, $ChildPath) -Recurse -Directory

                foreach ($ChildItem in ($Children | Where-Object -FilterScript {$_.Name -ne '.deployments' -or $_.Name -ne '.state'})) {
                    [string] $Location = [string]::Empty

                    if ($ChildItem.BaseName -match $SubscriptionRegex) {
                        [string] $Scope = 'Subscription'
                        [string] $ChildItemName = $SubscriptionRegex.Match($ChildItem.BaseName).Groups[0].Value
                    } elseif ($ChildItem.Parent -match $SubscriptionRegex) {
                        [string] $Scope = 'ResourceGroup'
                        [string] $ChildItemName = $LocationRegex.Match($ChildItem.BaseName).Groups[1].Value.Trim()
                        [string] $Location = $LocationRegex.Match($ChildItem.BaseName).Groups[2].Value
                    } else {
                        [string] $Scope = 'ManagementGroup'
                        [string] $ChildItemName = $ChildItem.Name
                    }

                    [AzBuilderScope] $ChildAzBuilderScope = [AzBuilderScope]::new($ChildItemName, $Scope, ('{0}' -f $ChildItem.FullName.ToLower().Replace(('{0}{1}' -f $Path.ToLower(), [System.IO.Path]::DirectorySeparatorChar), '')), $Location)
                    $AzBuilderList.Add($ChildAzBuilderScope)
                }
            }
        }

        [Microsoft.Azure.Commands.Resources.Models.ManagementGroups.PSManagementGroupInfo[]] $ManagementGroups = Get-AzManagementGroup
        [Microsoft.Azure.Commands.Profile.Models.PSAzureSubscription[]] $Subscriptions = Get-AzSubscription
        [System.Collections.Generic.List[Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResourceGroup]] $ResourceGroups = @()

        for ($i = 0; $i -lt $AzBuilderList.Count; $i++) {
            [System.IO.DirectoryInfo] $Item = Get-Item -Path ('{0}\{1}' -f $Path, $AzBuilderList[$i].Path)
            [System.IO.FileInfo[]] $Templates = $Item.GetFiles('*.json')

            if ($Templates) {
                $AzBuilderList[$i].Templates = GetTemplates $Templates
            }

            if ($AzBuilderList[$i].Scope -eq 'Tenant') {
                $AzBuilderList[$i].ParentId = $ManagementGroups | Where-Object -Property 'DisplayName' -eq $AzBuilderList[$i].Name | Select-Object -ExpandProperty 'Id'
            } elseif ($AzBuilderList[$i].Scope -eq 'ManagementGroup') {
                if (-not ($ManagementGroups.DisplayName -contains $AzBuilderList[$i].Name)) {
                    $AzBuilderList[$i].Deploy = $true
                }

                if ($ManagementGroups.DisplayName -contains $AzBuilderList[$i].Parent) {
                    $AzBuilderList[$i].ParentId = $ManagementGroups | Where-Object -Property 'DisplayName' -eq $AzBuilderList[$i].Parent | Select-Object -ExpandProperty 'Id'
                }
            } elseif ($AzBuilderList[$i].Scope -eq 'Subscription') {
                if ($Subscriptions.Id -contains $AzBuilderList[$i].Name) {
                    $AzBuilderList[$i].ParentId = $ManagementGroups | Where-Object -Property 'DisplayName' -eq $AzBuilderList[$i].Parent | Select-Object -ExpandProperty 'Id'

                    $null = Set-AzContext -Subscription $AzBuilderList[$i].Name

                    [Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResourceGroup[]] $ExistingResourceGroups = Get-AzResourceGroup

                    foreach ($ResourceGroup in $ExistingResourceGroups) {
                        $ResourceGroups.Add($ResourceGroup)
                    }
                }
            }
        }

        for ($i = 0; $i -lt $AzBuilderList.Count; $i++) {
            if ($AzBuilderList[$i].Scope -eq 'ResourceGroup') {
                if (-not ($ResourceGroups.ResourceGroupName -contains $AzBuilderList[$i].Name)) {
                    $AzBuilderList[$i].Deploy = $true
                }

                $AzBuilderList[$i].ParentId = '/subscriptions/{0}' -f $AzBuilderList[$i].Parent
            }
        }

        $AzBuilderList.Add($RootScope)

        return $AzBuilderList
    } catch {
        $PSCmdlet.ThrowTerminatingError($PSItem)
    }
}