internal/classes/AzOpsScope.ps1

class AzOpsScope {

    [string]$Scope
    [string]$Type
    [string]$Name
    [string]$StatePath
    [string]$ManagementGroup
    [string]$ManagementGroupDisplayName
    [string]$Subscription
    [string]$SubscriptionDisplayName
    [string]$ResourceGroup
    [string]$ResourceProvider
    [string]$Resource

    hidden [string]$StateRoot

    #region Internal Regex Helpers
    hidden [regex]$regex_tenant = '/$'
    hidden [regex]$regex_managementgroup = '(?i)^/providers/Microsoft.Management/managementgroups/[^/]+$'
    hidden [regex]$regex_managementgroupExtract = '(?i)^/providers/Microsoft.Management/managementgroups/'

    hidden [regex]$regex_subscription = '(?i)^/subscriptions/[^/]*$'
    hidden [regex]$regex_subscriptionExtract = '(?i)^/subscriptions/'

    hidden [regex]$regex_resourceGroup = '(?i)^/subscriptions/.*/resourcegroups/[^/]*$'
    hidden [regex]$regex_resourceGroupExtract = '(?i)^/subscriptions/.*/resourcegroups/'

    hidden [regex]$regex_managementgroupProvider = '(?i)^/providers/Microsoft.Management/managementgroups/[\s\S]*/providers'
    hidden [regex]$regex_subscriptionProvider = '(?i)^/subscriptions/.*/providers'
    hidden [regex]$regex_resourceGroupProvider = '(?i)^/subscriptions/.*/resourcegroups/[\s\S]*/providers'

    hidden [regex]$regex_managementgroupResource = '(?i)^/providers/Microsoft.Management/managementGroups/[\s\S]*/providers/[\s\S]*/[\s\S]*/'
    hidden [regex]$regex_subscriptionResource = '(?i)^/subscriptions/.*/providers/[\s\S]*/[\s\S]*/'
    hidden [regex]$regex_resourceGroupResource = '(?i)^/subscriptions/.*/resourcegroups/[\s\S]*/providers/[\s\S]*/[\s\S]*/'
    #endregion Internal Regex Helpers

    #region Constructors
    AzOpsScope ([string]$Scope, [string]$StateRoot) {
        <#
        .SYNOPSIS
            Creates an AzOpsScope based on the specified resource ID or File System Path

        .DESCRIPTION
            Creates an AzOpsScope based on the specified resource ID or File System Path

        .PARAMETER Scope
            Scope == ResourceID or File System Path

        .INPUTS
            None. You cannot pipe objects to Add-Extension.

        .OUTPUTS
            System.String. Add-Extension returns a string with the extension or file name.

        .EXAMPLE
            New-AzOpsScope -Scope "/providers/Microsoft.Management/managementGroups/3fc1081d-6105-4e19-b60c-1ec1252cf560"

            Creates an AzOpsScope based on the specified resource ID
    #>

        Write-PSFMessage -Level Verbose -String 'AzOpsScope.Constructor' -StringValues $scope -FunctionName AzOpsScope -ModuleName AzOps
        $this.StateRoot = $StateRoot
        if (Test-Path -Path $scope) {
            if ((Get-Item $scope).GetType().ToString() -eq 'System.IO.FileInfo') {
                #Strong confidence based on content - file
                Write-PSFMessage -Level Verbose -String 'AzOpsScope.InitializeMemberVariablesFromFile' -StringValues $scope -FunctionName AzOpsScope -ModuleName AzOps
                $this.InitializeMemberVariablesFromFile($Scope)
            }
            else {
                # Weak confidence based on metadata at scope - directory
                Write-PSFMessage -Level Verbose -String 'AzOpsScope.InitializeMemberVariablesFromDirectory' -StringValues $scope -FunctionName AzOpsScope -ModuleName AzOps
                $this.InitializeMemberVariablesFromDirectory($Scope)
            }
        }
        else {
            Write-PSFMessage -Level Verbose -String 'AzOpsScope.InitializeMemberVariables' -StringValues $scope -FunctionName AzOpsScope -ModuleName AzOps
            $this.InitializeMemberVariables($Scope)
        }
    }

    # Overloaded constructors - repeat member assignments in each constructor definition
    #AzOpsScope ([System.IO.DirectoryInfo]$Path, [string]$StateRoot) {
    hidden [void] InitializeMemberVariablesFromDirectory([System.IO.DirectoryInfo]$Path) {

        $managementGroupFileName = "microsoft.management_managementGroups-*$(Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix')"
        $subscriptionFileName = "microsoft.subscription_subscriptions-*$(Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix')"
        $resourceGroupFileName = "microsoft.resources_resourceGroups-*$(Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix')"

        if ($Path.FullName -eq (Get-Item $this.StateRoot).FullName) {
            # Root tenant path
            Write-PSFMessage -Level Verbose -String 'AzOpsScope.InitializeMemberVariablesFromDirectory.RootTenant' -StringValues $Path -FunctionName InitializeMemberVariablesFromDirectory -ModuleName AzOps
            $this.InitializeMemberVariables("/")
            return
        }
        # Always look into AutoGeneratedTemplateFolderPath folder regardless of path specified
        if ($Path.FullName -notlike "*$(Get-PSFConfigValue -FullName 'AzOps.Core.AutoGeneratedTemplateFolderPath')") {
            $Path = Join-Path $Path -ChildPath "$(Get-PSFConfigValue -FullName 'AzOps.Core.AutoGeneratedTemplateFolderPath')"
            Write-PSFMessage -Level Verbose -String 'AzOpsScope.InitializeMemberVariablesFromDirectory.AutoGeneratedFolderPath' -StringValues $Path -FunctionName InitializeMemberVariablesFromDirectory -ModuleName AzOps
        }

        if ($managementGroupScopeFile = (Get-ChildItem -Force -Path $Path -File | Where-Object Name -like $managementGroupFileName)) {
            [string] $managementGroupID = $managementGroupScopeFile.Name.Replace('microsoft.management_managementgroups-', '').Replace('.parameters', '').Replace($(Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix'), '') 
            Write-PSFMessage -Level Verbose -String 'AzOpsScope.Input.FromFileName.ManagementGroup' -StringValues $managementGroupID -FunctionName InitializeMemberVariablesFromDirectory -ModuleName AzOps
            $this.InitializeMemberVariables("/providers/Microsoft.Management/managementGroups/$managementGroupID")
        }
        elseif ($subscriptionScopeFileName = (Get-ChildItem -Force -Path $Path -File | Where-Object Name -like $subscriptionFileName)) {
            [string] $subscriptionID = $subscriptionScopeFileName.Name.Replace('microsoft.subscription_subscriptions-', '').Replace('.parameters', '').Replace($(Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix'), '')
            Write-PSFMessage -Level Verbose -String 'AzOpsScope.Input.FromFileName.Subscription' -StringValues $subscriptionID -FunctionName InitializeMemberVariablesFromDirectory -ModuleName AzOps
            $this.InitializeMemberVariables("/subscriptions/$subscriptionID")
        }
        elseif ((Get-ChildItem -Force -Path $Path -File | Where-Object Name -like $resourceGroupFileName) -or
            ((Get-ChildItem -Force -Path $Path.Parent -File | Where-Object Name -like $subscriptionFileName))
        ) {
            Write-PSFMessage -Level Verbose -String 'AzOpsScope.InitializeMemberVariablesFromDirectory.ParentSubscription' -StringValues $Path.Parent -FunctionName InitializeMemberVariablesFromDirectory -ModuleName AzOps
            $parent = (New-AzOpsScope -Path $Path.Parent)
            $this.InitializeMemberVariables($("/subscriptions/{0}/resourceGroups/{1}" -f $parent.Subscription, $Path.Name))
        }
        else {
            #Error
            Write-PSFMessage -Level Warning -Tag error -String 'AzOpsScope.Input.BadData.UnknownType' -StringValues $Path -FunctionName AzOpsScope -ModuleName AzOps
            throw "Invalid File Structure! Cannot find Management Group / Subscription / Resource Group files in $Path!"
        }
    }

    #AzOpsScope ([System.IO.FileInfo]$Path, [string]$StateRoot) {
    hidden [void] InitializeMemberVariablesFromFile([System.IO.FileInfo]$Path) {
        if (-not $Path.Exists) { throw 'Invalid Input!' }

        if ($Path.Extension -ne '.json') {
            # Try to determine based on directory
            Write-PSFMessage -Level Verbose -String 'AzOpsScope.InitializeMemberVariablesFromFile.NotJson' -StringValues $Path -FunctionName InitializeMemberVariablesFromFile -ModuleName AzOps
            $this.InitializeMemberVariablesFromDirectory($Path.Directory)
            return
        }
        else {
            $resourcePath = Get-Content $Path | ConvertFrom-Json -AsHashtable

            if (!$resourcePath) {
                # Empty file with .json is not valid JSON file. Empty Json should've minimum file content '{}'
                # However, due to bug that is combination of Get-Content and ConvertFrom-Json when empty file with .json (that is valid file but not valid Json),
                # switch statement is failing to handle $null value unless assigned explicitly.
                $resourcePath = $null
            }

            switch ($resourcePath) {
                { $_.parameters.input.value.Keys -contains "ResourceId" } {
                    # Parameter Files - resource from parameters file
                    Write-PSFMessage -Level Verbose -String 'AzOpsScope.InitializeMemberVariablesFromFile.ResourceId' -StringValues $($resourcePath.parameters.input.value.ResourceId) -FunctionName InitializeMemberVariablesFromFile -ModuleName AzOps
                    $this.InitializeMemberVariables($resourcePath.parameters.input.value.ResourceId)
                    break
                }
                { $_.parameters.input.value.Keys -contains "Id" } {
                    # Parameter Files - ManagementGroup and Subscription
                    Write-PSFMessage -Level Verbose -String 'AzOpsScope.InitializeMemberVariablesFromFile.Id' -StringValues $($resourcePath.parameters.input.value.Id) -FunctionName InitializeMemberVariablesFromFile -ModuleName AzOps
                    $this.InitializeMemberVariables($resourcePath.parameters.input.value.Id)
                    break
                }
                { $_.parameters.input.value.Keys -contains "Type" } {
                    # Parameter Files - Determine Resource Type and Name (Management group)
                    # Management group resource id do contain '/provider'
                    Write-PSFMessage -Level Verbose -String 'AzOpsScope.InitializeMemberVariablesFromFile.Type' -StringValues ("$($resourcePath.parameters.input.value.Type)/$($resourcePath.parameters.input.value.Name)") -FunctionName InitializeMemberVariablesFromFile -ModuleName AzOps
                    $this.InitializeMemberVariables("$($resourcePath.parameters.input.value.Type)/$($resourcePath.parameters.input.value.Name)")
                    break
                }
                { $_.parameters.input.value.Keys -contains "ResourceType" } {
                    # Parameter Files - Determine Resource Type and Name (Any ResourceType except management group)
                    Write-PSFMessage -Level Verbose -String 'AzOpsScope.InitializeMemberVariablesFromFile.ResourceType' -StringValues ($resourcePath.parameters.input.value.ResourceType) -FunctionName InitializeMemberVariablesFromFile -ModuleName AzOps
                    $currentScope = New-AzOpsScope -Path ($Path.Directory)

                    # Creating Resource Id based on current scope, resource Type and Name of the resource
                    $this.InitializeMemberVariables("$($currentScope.scope)/providers/$($resourcePath.parameters.input.value.ResourceType)/$($resourcePath.parameters.input.value.Name)")
                    break
                }
                { $_.resources -and
                    $_.resources[0].type -eq 'Microsoft.Management/managementGroups' } {
                    # Template - Management Group
                    Write-PSFMessage -Level Verbose -String 'AzOpsScope.InitializeMemberVariablesFromFile.managementgroups' -StringValues ($_.resources[0].name) -FunctionName InitializeMemberVariablesFromFile -ModuleName AzOps
                    $this.InitializeMemberVariables("/providers/Microsoft.Management/managementgroups/{0}" -f ($_.resources[0].name) )
                    break
                }
                { $_.resources -and
                    $_.resources[0].type -eq 'Microsoft.Management/managementGroups/subscriptions' } {
                    # Template - Subscription
                    Write-PSFMessage -Level Verbose -String 'AzOpsScope.InitializeMemberVariablesFromFile.subscriptions' -StringValues ($_.resources[0].name) -FunctionName InitializeMemberVariablesFromFile -ModuleName AzOps
                    $this.InitializeMemberVariables("/subscriptions/{0}" -f ($_.resources[0].name -split '/' | Select-Object -Last 1) )
                    break
                }
                { $_.resources -and
                    $_.resources[0].type -eq 'Microsoft.Resources/resourceGroups' } {
                    Write-PSFMessage -Level Verbose -String 'Az OpsScope.InitializeMemberVariablesFromFile.resourceGroups' -StringValues ($_.resources[0].name) -FunctionName InitializeMemberVariablesFromFile -ModuleName AzOps
                    #Get the name of subscription from parent scope
                    $currentScope = New-AzOpsScope -Path ($Path.Directory.Parent)

                    #Initialize resource group
                    $this.InitializeMemberVariables("$($currentScope.scope)/resourceGroups/{0}" -f $_.resources[0].name )
                    break
                }
                { $_.resources } {
                    # Template - 1st resource
                    Write-PSFMessage -Level Verbose -String 'Az OpsScope.InitializeMemberVariablesFromFile.resource' -StringValues ($_.resources[0].type), ($_.resources[0].name) -FunctionName InitializeMemberVariablesFromFile -ModuleName AzOps
                    $currentScope = New-AzOpsScope -Path ($Path.Directory)
                    $this.InitializeMemberVariables("$($currentScope.scope)/providers/$($_.resources[0].type)/$($_.resources[0].name)")
                    break
                }
                Default {
                    Write-PSFMessage -Level Warning  -String 'AzOpsScope.Input.BadData.TemplateParameterFile' -StringValues $Path -FunctionName AzOpsScope -ModuleName AzOps
                    Write-PSFMessage -Level Verbose -String 'AzOpsScope.InitializeMemberVariablesFromDirectory' -StringValues $Path -FunctionName AzOpsScope -ModuleName AzOps
                    $this.InitializeMemberVariablesFromDirectory($Path.Directory)
                }
            }
        }
    }
    #endregion Constructors

    hidden [void] InitializeMemberVariables([string]$Scope) {
        Write-PSFMessage -Level Verbose -String 'AzOpsScope.InitializeMemberVariables.Start' -StringValues ($scope) -FunctionName InitializeMemberVariables -ModuleName AzOps
        $this.Scope = $Scope

        if ($this.IsResource()) {
            $this.Type = "resource"
            $this.Name = $this.IsResource()
            $this.Subscription = $this.GetSubscription()
            $this.SubscriptionDisplayName = $this.GetSubscriptionDisplayName()
            $this.ManagementGroup = $this.GetManagementGroup()
            $this.ManagementGroupDisplayName = $this.GetManagementGroupName()
            $this.ResourceGroup = $this.GetResourceGroup()
            $this.ResourceProvider = $this.IsResourceProvider()
            $this.Resource = $this.GetResource()
            if (Get-PSFConfigValue -FullName AzOps.Core.ExportRawTemplate) {
                $this.StatePath = $this.GetAzOpsResourcePath() + ".json"
            }
            else {
                if ( (Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix') -notcontains 'parameters.json' -and
                    ("$($this.ResourceProvider)/$($this.Resource)" -eq 'Microsoft.Authorization/policyDefinitions')
                ) {
                    $this.StatePath = ($this.GetAzOpsResourcePath() + '.parameters' + (Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix'))
                }
                else {
                    $this.StatePath = ($this.GetAzOpsResourcePath() + (Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix'))
                }
            }
        }
        elseif ($this.IsResourceGroup()) {
            $this.Type = "resourcegroups"
            $this.ResourceProvider = "Microsoft.Resources"
            $this.Resource = "resourceGroups"
            $this.Name = $this.IsResourceGroup()
            $this.Subscription = $this.GetSubscription()
            $this.SubscriptionDisplayName = $this.GetSubscriptionDisplayName()
            $this.ManagementGroup = $this.GetManagementGroup()
            $this.ManagementGroupDisplayName = $this.GetManagementGroupName()
            $this.ResourceGroup = $this.GetResourceGroup()
            if (Get-PSFConfigValue -FullName AzOps.Core.ExportRawTemplate) {
                $this.StatePath = (Join-Path $this.GetAzOpsResourceGroupPath() -ChildPath ("$(Get-PSFConfigValue -FullName 'AzOps.Core.AutoGeneratedTemplateFolderPath')\microsoft.resources_resourcegroups-$($this.ResourceGroup).json").ToLower() )
            }
            else {
                $this.StatePath = (Join-Path $this.GetAzOpsResourceGroupPath() -ChildPath ("$(Get-PSFConfigValue -FullName 'AzOps.Core.AutoGeneratedTemplateFolderPath')\microsoft.resources_resourcegroups-$($this.ResourceGroup)" + $(Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix')).ToLower())
            }
        }
        elseif ($this.IsSubscription()) {
            $this.Type = "subscriptions"
            $this.ResourceProvider = "Microsoft.Management"
            $this.Resource = "managementGroups/subscriptions"
            $this.Name = $this.IsSubscription()
            $this.Subscription = $this.GetSubscription()
            $this.SubscriptionDisplayName = $this.GetSubscriptionDisplayName()
            if ($script:AzOpsAzManagementGroup) {
                $this.ManagementGroup = $this.GetManagementGroup()
                $this.ManagementGroupDisplayName = $this.GetManagementGroupName()
            }
            if (Get-PSFConfigValue -FullName AzOps.Core.ExportRawTemplate) {
                $this.StatePath = (Join-Path $this.GetAzOpsSubscriptionPath() -ChildPath ("$(Get-PSFConfigValue -FullName 'AzOps.Core.AutoGeneratedTemplateFolderPath')\microsoft.subscription_subscriptions-$($this.Subscription).json").ToLower())
            }
            else {
                $this.StatePath = (Join-Path $this.GetAzOpsSubscriptionPath() -ChildPath (("$(Get-PSFConfigValue -FullName 'AzOps.Core.AutoGeneratedTemplateFolderPath')\microsoft.subscription_subscriptions-$($this.Subscription)" + $(Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix'))).ToLower())
            }

        }
        elseif ($this.IsManagementGroup()) {
            $this.Type = "managementGroups"
            $this.ResourceProvider = "Microsoft.Management"
            $this.Resource = "managementGroups"
            $this.Name = $this.GetManagementGroup()
            $this.ManagementGroup = ($this.GetManagementGroup()).Trim()
            $this.ManagementGroupDisplayName = ($this.GetManagementGroupName()).Trim()
            if (Get-PSFConfigValue -FullName AzOps.Core.ExportRawTemplate) {
                $this.StatePath = (Join-Path $this.GetAzOpsManagementGroupPath($this.ManagementGroup) -ChildPath ("$(Get-PSFConfigValue -FullName 'AzOps.Core.AutoGeneratedTemplateFolderPath')\microsoft.management_managementgroups-$($this.ManagementGroup).json").ToLower())
            }
            else {
                #For management group, always create parameter file
                if ( (Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix') -notcontains 'parameters.json') {
                    $this.StatePath = (Join-Path $this.GetAzOpsManagementGroupPath($this.ManagementGroup) -ChildPath (("$(Get-PSFConfigValue -FullName 'AzOps.Core.AutoGeneratedTemplateFolderPath')\microsoft.management_managementgroups-$($this.ManagementGroup)" + '.parameters' + $(Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix'))).ToLower())
                }
                else {
                    $this.StatePath = (Join-Path $this.GetAzOpsManagementGroupPath($this.ManagementGroup) -ChildPath (("$(Get-PSFConfigValue -FullName 'AzOps.Core.AutoGeneratedTemplateFolderPath')\microsoft.management_managementgroups-$($this.ManagementGroup)" + $(Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix'))).ToLower())
                }
            }
        }
        elseif ($this.IsRoot()) {
            $this.Type = "root"
            $this.Name = "/"
            $this.StatePath = $this.StateRoot.ToLower()
        }
        Write-PSFMessage -Level Verbose -String 'AzOpsScope.InitializeMemberVariables.End' -StringValues ($scope) -FunctionName InitializeMemberVariables -ModuleName AzOps
    }
    #endregion Initializers

    [String] ToString() {
        return $this.Scope
    }

    #region Validators
    [bool] IsRoot() {
        if (($this.Scope -match $this.regex_tenant)) {
            return $true
        }
        return $false
    }
    [bool] IsManagementGroup() {
        if (($this.Scope -match $this.regex_managementgroup)) {
            return $true
        }
        return $false
    }

    [string] IsSubscription() {
        if (($this.Scope -match $this.regex_subscription)) {
            return ($this.Scope.Split('/')[2])
        }
        return $null
    }
    [string] IsResourceGroup() {
        if (($this.Scope -match $this.regex_resourceGroup)) {
            return ($this.Scope.Split('/')[4])
        }
        return $null
    }
    [string] IsResourceProvider() {

        if ($this.Scope -match $this.regex_managementgroupProvider) {
            return (($this.regex_managementgroupProvider.Split($this.Scope) | Select-Object -last 1) -split '/')[1]
        }
        if ($this.Scope -match $this.regex_subscriptionProvider) {
            return (($this.regex_subscriptionProvider.Split($this.Scope) | Select-Object -last 1) -split '/')[1]
        }
        if ($this.Scope -match $this.regex_resourceGroupProvider) {
            return (($this.regex_resourceGroupProvider.Split($this.Scope) | Select-Object -last 1) -split '/')[1]
        }

        return $null
    }
    [string] IsResource() {

        if ($this.Scope -match $this.regex_managementgroupResource) {
            return ($this.regex_managementgroupResource.Split($this.Scope) | Select-Object -last 1)
        }
        if ($this.Scope -match $this.regex_subscriptionResource) {
            return ($this.regex_subscriptionResource.Split($this.Scope) | Select-Object -last 1)
        }
        if ($this.Scope -match $this.regex_resourceGroupResource) {
            return ($this.regex_resourceGroupResource.Split($this.Scope) | Select-Object -last 1)
        }
        return $null
    }
    #endregion Validators

    #region Data Accessors
    <#
        Should Return Management Group Name
    #>

    [string] GetManagementGroup() {

        if ($this.GetManagementGroupName()) {
            foreach ($mgmt in $script:AzOpsAzManagementGroup) {
                if ($mgmt.DisplayName -eq $this.GetManagementGroupName()) {
                    return $mgmt.Name
                }
            }
        }
        if ($this.Subscription) {
            foreach ($mgmt in $script:AzOpsAzManagementGroup) {
                foreach ($child in $mgmt.Children) {
                    if ($child.DisplayName -eq $this.subscriptionDisplayName) {
                        return $mgmt.Name
                    }
                }
            }
        }
        return $null
    }

    [string] GetAzOpsManagementGroupPath([string]$managementgroupName) {
        if ($groupObject = $script:AzOpsAzManagementGroup | Where-Object Name -eq $managementgroupName) {
            $parentMgName = $groupObject.parentId -split "/" | Select-Object -Last 1
            $parentObject = $script:AzOpsAzManagementGroup | Where-Object Name -eq $parentMgName
            if ($groupObject.parentId -and $parentObject) {
                $parentPath = $this.GetAzOpsManagementGroupPath($parentMgName)
                $childPath = "{0} ({1})" -f $groupObject.DisplayName, $groupObject.Name
                return Join-Path $parentPath -ChildPath ($childPath.ToLower())
            }
            else {
                $childPath = "{0} ({1})" -f $groupObject.DisplayName, $groupObject.Name
                return Join-Path $this.StateRoot -ChildPath ($childPath.ToLower())
            }
        }
        else {
            Write-PSFMessage -Level Warning -Tag error -String 'AzOpsScope.GetAzOpsManagementGroupPath.NotFound' -StringValues $managementgroupName -FunctionName AzOpsScope -ModuleName AzOps
            throw "Management Group not found: $managementgroupName"
        }
    }

    <#
        Should Return Management Group Display Name
    #>

    [string] GetManagementGroupName() {
        if ($this.Scope -match $this.regex_managementgroupExtract) {
            $mgId = $this.Scope -split $this.regex_managementgroupExtract -split '/' | Where-Object { $_ } | Select-Object -First 1

            if ($mgId) {
                $mgDisplayName = ($script:AzOpsAzManagementGroup | Where-Object Name -eq $mgId).DisplayName
                if ($mgDisplayName) {
                    #Write-PSFMessage -Level Debug -String 'AzOpsScope.GetManagementGroupName.Found.Azure' -StringValues $mgDisplayName -FunctionName AzOpsScope -ModuleName AzOps
                    return $mgDisplayName
                }
                else {
                    Write-PSFMessage -Level Debug -String 'AzOpsScope.GetManagementGroupName.NotFound' -StringValues $mgId -FunctionName AzOpsScope -ModuleName AzOps
                    return $mgId
                }
            }
        }
        if ($this.Subscription) {
            foreach ($managementGroup in $script:AzOpsAzManagementGroup) {
                foreach ($child in $managementGroup.Children) {
                    if ($child.DisplayName -eq $this.subscriptionDisplayName) {
                        return $managementGroup.DisplayName
                    }
                }
            }
        }
        return $null
    }
    [string] GetAzOpsSubscriptionPath() {
        $childpath = "{0} ({1})" -f $this.SubscriptionDisplayName, $this.Subscription
        if ($script:AzOpsAzManagementGroup) {
            return (Join-Path $this.GetAzOpsManagementGroupPath($this.ManagementGroup) -ChildPath ($childpath).ToLower())
        }
        else {
            return (Join-Path $this.StateRoot -ChildPath ($childpath).ToLower())
        }
    }
    [string] GetAzOpsResourceGroupPath() {

        return (Join-Path $this.GetAzOpsSubscriptionPath() -ChildPath ($this.ResourceGroup).ToLower())
    }
    [string] GetSubscription() {
        if ($this.Scope -match $this.regex_subscriptionExtract) {

            $subId = $this.Scope -split $this.regex_subscriptionExtract -split '/' | Where-Object { $_ } | Select-Object -First 1
            $sub = $script:AzOpsSubscriptions | Where-Object subscriptionId -eq $subId
            if ($sub) {
                Write-PSFMessage -Level Debug -String 'AzOpsScope.GetSubscription.Found' -StringValues $sub.Id -FunctionName AzOpsScope -ModuleName AzOps
                return $sub.subscriptionId
            }
            else {
                Write-PSFMessage -Level Debug -String 'AzOpsScope.GetSubscription.NotFound' -StringValues $subId -FunctionName AzOpsScope -ModuleName AzOps
                return $subId
            }
        }
        return $null
    }
    [string] GetSubscriptionDisplayName() {
        if ($this.Scope -match $this.regex_subscriptionExtract) {

            $subId = $this.Scope -split $this.regex_subscriptionExtract -split '/' | Where-Object { $_ } | Select-Object -First 1
            $sub = $script:AzOpsSubscriptions | Where-Object subscriptionId -eq $subId
            if ($sub) {
                Write-PSFMessage -Level Debug -String 'AzOpsScope.GetSubscriptionDisplayName.Found' -StringValues $sub.displayName -FunctionName AzOpsScope -ModuleName AzOps
                return $sub.displayName
            }
            else {
                Write-PSFMessage -Level Debug -String 'AzOpsScope.GetSubscriptionDisplayName.NotFound' -StringValues $subId -FunctionName AzOpsScope -ModuleName AzOps
                return $subId
            }
        }
        return $null
    }
    [string] GetResourceGroup() {

        if ($this.Scope -match $this.regex_resourceGroupExtract) {
            return ($this.Scope -split $this.regex_resourceGroupExtract -split '/' | Where-Object { $_ } | Select-Object -First 1)
        }
        return $null
    }
    [string] GetResource() {

        if ($this.Scope -match $this.regex_managementgroupProvider) {
            return (($this.regex_managementgroupProvider.Split($this.Scope) | Select-Object -last 1) -split '/')[2]
        }
        if ($this.Scope -match $this.regex_subscriptionProvider) {
            return (($this.regex_subscriptionProvider.Split($this.Scope) | Select-Object -last 1) -split '/')[2]
        }
        if ($this.Scope -match $this.regex_resourceGroupProvider) {
            return (($this.regex_resourceGroupProvider.Split($this.Scope) | Select-Object -last 1) -split '/')[2]
        }

        return $null
    }

    [string] GetAzOpsResourcePath() {

        Write-PSFMessage -Level Debug -String 'AzOpsScope.GetAzOpsResourcePath.Retrieving' -StringValues $this.Scope -FunctionName AzOpsScope -ModuleName AzOps
        if ($this.Scope -match $this.regex_resourceGroupResource) {
            $rgpath = $this.GetAzOpsResourceGroupPath()
            return (Join-Path (Join-Path $rgpath -ChildPath "$(Get-PSFConfigValue -FullName 'AzOps.Core.AutoGeneratedTemplateFolderPath')") -ChildPath ($this.ResourceProvider + "_" + $this.Resource + "-" + $this.Name).ToLower())
        }
        elseif ($this.Scope -match $this.regex_subscriptionResource) {
            $subpath = $this.GetAzOpsSubscriptionPath()
            return (Join-Path (Join-Path $subpath -ChildPath "$(Get-PSFConfigValue -FullName 'AzOps.Core.AutoGeneratedTemplateFolderPath')") -ChildPath ($this.ResourceProvider + "_" + $this.Resource + "-" + $this.Name).ToLower())
        }
        elseif ($this.Scope -match $this.regex_managementgroupResource) {
            $mgmtPath = $this.GetAzOpsManagementGroupPath($this.ManagementGroup)
            return (Join-Path (Join-Path $mgmtPath -ChildPath "$(Get-PSFConfigValue -FullName 'AzOps.Core.AutoGeneratedTemplateFolderPath')") -ChildPath ($this.ResourceProvider + "_" + $this.Resource + "-" + $this.Name).ToLower())
        }
        Write-PSFMessage -Level Warning -Tag error -String 'AzOpsScope.GetAzOpsResourcePath.NotFound' -StringValues $this.Scope -FunctionName AzOpsScope -ModuleName AzOps
        throw "Unable to determine Resource Scope for: $($this.Scope)"
    }
    #endregion Data Accessors
}