Framework/Core/SubscriptionSecurity/ARMPolicies.ps1

using namespace System.Management.Automation
Set-StrictMode -Version Latest 

# Class to implement Subscription ARM Policy controls
class ARMPolicy: AzCommandBase
{    
    hidden [ARMPolicyModel] $ARMPolicyObj = $null;
    hidden [PolicyInitiative] $SubPolicyInitiative = $null;
    hidden [bool] $UpdateInitiative = $false;
    static [string] $PolicyProviderNamespace = "Microsoft.PolicyInsights";
    
    hidden [PSObject[]] $ApplicableARMPolicies = $null;
    #hidden [PSObject[]] $PolicyAssignments = $null;

    ARMPolicy([string] $subscriptionId, [InvocationInfo] $invocationContext, [string] $tags, [bool] $updateInitiative): 
        Base($subscriptionId, $invocationContext)
    { 
        $this.ARMPolicyObj = [ARMPolicyModel] $this.LoadServerConfigFile("Subscription.ARMPolicies.json"); 
        $isPolicyInitiativeEnabled = [FeatureFlightingManager]::GetFeatureStatus("EnableAzurePolicyBasedScan",$($this.SubscriptionContext.SubscriptionId))
        if($isPolicyInitiativeEnabled)
        {
            $this.SubPolicyInitiative = [PolicyInitiative] $this.LoadServerConfigFile("Subscription.Initiative.json"); 
        }
        $this.FilterTags = $this.ConvertToStringArray($tags);
        $this.UpdateInitiative = $updateInitiative;
    }

    hidden [PSObject[]] GetApplicableARMPolicies()
    {
        if($null -eq $this.ApplicableARMPolicies)
        {
            $this.ApplicableARMPolicies = @();

            $subscriptionId = $this.SubscriptionContext.SubscriptionId;
            if(($this.FilterTags | Measure-Object).Count -ne 0)
            {
                $this.ARMPolicyObj.Policies | 
                    ForEach-Object {
                        $currentItem = $_;
                        if(($currentItem.Tags | Where-Object { $this.FilterTags -Contains $_ } | Measure-Object).Count -ne 0)
                        {
                            # Resolve the value of SubscriptionId
                            $currentItem.Scope = $global:ExecutionContext.InvokeCommand.ExpandString($currentItem.Scope);
                            if([string]::IsNullOrWhiteSpace($currentItem.Scope))
                            {
                                $currentItem.Scope = "/subscriptions/$subscriptionId"
                            }

                            $this.ApplicableARMPolicies  += $currentItem;
                        }
                    }
            }
            else
            {
                $this.ApplicableARMPolicies += $this.ARMPolicyObj.Policies
            }
        }
            
        return $this.ApplicableARMPolicies;
    }

    [MessageData[]] SetARMPolicies()
    {
        [ResourceHelper]::RegisterResourceProviderIfNotRegistered([ARMPolicy]::PolicyProviderNamespace);
        [MessageData[]] $messages = @();
        $this.RemoveDeprecatedPolicies();
        if(($this.ARMPolicyObj.Policies | Measure-Object).Count -ne 0)
        {
            if($this.GetApplicableARMPolicies() -ne 0)
            {
                $startMessage = [MessageData]::new("Processing AzSK ARM policies. Total policies: $($this.GetApplicableARMPolicies().Count)");
                $messages += $startMessage;
                $this.PublishCustomMessage($startMessage);
                
                $disabledPolicies = $this.GetApplicableARMPolicies() | Where-Object { -not $_.Enabled };
                if(($disabledPolicies | Measure-Object).Count -ne 0)
                {
                    $disabledMessage = "Found ARM policies which are disabled. Total disabled policies: $($disabledPolicies.Count)";
                    $messages += [MessageData]::new($disabledMessage, $disabledPolicies);
                    $this.PublishCustomMessage($disabledMessage, [MessageType]::Warning);
                }

                $enabledPolicies = @();
                $enabledPolicies += $this.GetApplicableARMPolicies() | Where-Object { $_.Enabled };
                if($enabledPolicies.Count -ne 0)
                {
                    $messages += [MessageData]::new([Constants]::SingleDashLine + "`r`nAdding following ARM policies to the subscription. Total policies: $($enabledPolicies.Count)", $enabledPolicies);                                                                            
                    $armPoliciesDefns = @{};
                    $errorCount = 0;
                    [MessageData[]] $resultMessages = @();
                    $enabledPolicies | ForEach-Object {
                        $policyName = $_.PolicyDefinitionName;
                        $armPolicy = $null;
                        try {
                            $armPolicy = Get-AzPolicyDefinition -Name $policyName -ErrorAction Stop
                            try{
                            $temp = $_;        
                            $armpolicyassignment = Get-AzPolicyAssignment -Name $policyName
                            }
                            catch{
                            $armPoliciesDefns.Add($temp,$armPolicy);
                            }
                        }
                        catch {
                            #eat the exception if the policy is not found
                        }
                        if($null -eq $armPolicy)
                        {
                            # Add ARM policy
                            try
                            {
                                $armPolicy = New-AzPolicyDefinition -Name $policyName -Description $_.Description -Policy ([string]$_.PolicyDefinition) -ErrorAction Stop
                                $armPoliciesDefns.Add($_,$armPolicy);
                            }
                            catch
                            {
                                $messages += [MessageData]::new("Error while adding ARM policy [$policyName] to the subscription", $_, [MessageType]::Error);
                                $errorCount += 1;
                            }
                        }                            
                    };
                    if($errorCount -eq $enabledPolicies.Count )
                    {
                        $resultMessages += [MessageData]::new("No AzSK ARM policies were added to the subscription due to some error. See the log file for details.`r`n" + [Constants]::SingleDashLine, [MessageType]::Error);
                    }
                    elseif($errorCount -gt 0)
                    {
                        $resultMessages += [MessageData]::new("$errorCount/$($enabledPolicies.Count) ARM policy(ies) have not been added to the subscription. See the log file for details.", [MessageType]::Error);
                    }
                    $errorCount = 0;
                    $currentCount = 0;
                    if(($armPoliciesDefns.Keys | Measure-Object).Count -gt 0)
                    {
                        Start-Sleep -Seconds 15                                                            
                        $armPoliciesDefns.Keys | ForEach-Object {
                            $armPolicy = $_;
                            $armPolicyDefn = $armPoliciesDefns[$_];
                            $policyName = $armPolicy.PolicyDefinitionName;
                            $currentCount += 1;
                            # Add ARM policy
                            try
                            {                                
                                New-AzPolicyAssignment -Name $policyName -PolicyDefinition $armPolicyDefn  -Scope $armPolicy.Scope -ErrorAction Stop | Out-Null
                            }
                            catch
                            {
                                $messages += [MessageData]::new("Error while adding ARM policy [$policyName] to the subscription", $armPolicy, [MessageType]::Error);
                                $errorCount += 1;
                            }
                            $this.CommandProgress($enabledPolicies.Count, $currentCount, 2);
                        };
                    }
                    if($errorCount -eq 0)
                    {
                        #setting the version tag at AzSKRG
                        $azskRGName = [ConfigurationManager]::GetAzSKConfigData().AzSKRGName;
                        [ResourceGroupHelper]::SetResourceGroupTags($azskRGName,@{[Constants]::ARMPolicyConfigVersionTagName=$this.ARMPolicyObj.Version}, $false)
                    
                    $resultMessages += [MessageData]::new("All AzSK ARM policies have been added to the subscription successfully`r`n" + [Constants]::SingleDashLine, [MessageType]::Update);
                    }
                    elseif($errorCount -eq $enabledPolicies.Count)
                    {
                        $resultMessages += [MessageData]::new("No AzSK ARM policies were added to the subscription due to an error. Please add the ARM policies manually.`r`n" + [Constants]::SingleDashLine, [MessageType]::Error);
                    }
                    else
                    {
                        $resultMessages += [MessageData]::new("$errorCount/$($enabledPolicies.Count) ARM policy(ies) have not been added to the subscription. Please add the ARM policies manually or contact AzSK support team.", [MessageType]::Error);
                        $resultMessages += [MessageData]::new("$($enabledPolicies.Count - $errorCount)/$($enabledPolicies.Count) ARM policy(ies) have been added to the subscription successfully`r`n" + [Constants]::SingleDashLine, [MessageType]::Update);
                    }

                    $messages += $resultMessages;
                    $this.PublishCustomMessage($resultMessages);
                }
            }
            else
            {
                $this.PublishCustomMessage("No ARM policies have been found that matches the specified tags. Tags:[$([string]::Join(",", $this.FilterTags))].", [MessageType]::Warning);
            }
        }
        $messages += $this.SetPolicyInitiative();
        return $messages;
    }

    [void] RemoveDeprecatedPolicies()
    {
        if(($this.ARMPolicyObj.DeprecatedPolicies | Measure-Object).Count -gt 0)
        {
            $DeprecatedPolicyDefns = @();
            $this.ARMPolicyObj.DeprecatedPolicies | ForEach-Object {
                try
                {
                    $PolicyName = $_;
                    $PolicyDefn = Get-AzPolicyDefinition -Name $PolicyName -ErrorAction SilentlyContinue
                    if($null -ne $PolicyDefn)
                    {
                        $DeprecatedPolicyDefns += $PolicyDefn;
                    }
                }
                catch
                {
                    #eat this exception as silently continue is not working for this scenario
                }
            }
            if(($DeprecatedPolicyDefns | Measure-Object).Count -gt 0)
            {
                $assignments = @();
                $DeprecatedPolicyDefns | ForEach-Object {
                    $defn = $_;
                    $tassignment = Get-AzPolicyAssignment -PolicyDefinitionId $defn.policyDefinitionId  -ErrorAction SilentlyContinue
                    if($null -ne $tassignment)
                    {
                        $assignments += $tassignment;
                    }
                }
                if(($assignments | Measure-Object).Count -gt 0)
                {
                    $assignments | ForEach-Object {
                        $assigment = $_;
                        Remove-AzPolicyAssignment -Scope $assigment.properties.scope -Name $assigment.Name -ErrorAction SilentlyContinue
                    }                        
                    Start-Sleep -Seconds 15
                }                
                $DeprecatedPolicyDefns | ForEach-Object {
                    try
                    {
                        $defn = $_;
                        Remove-AzPolicyDefinition -Id $defn.PolicyDefinitionId -Force -ErrorAction SilentlyContinue        
                    }
                    catch
                    {
                        #todo eat the exception as it might throw error if it is being used in any of initiatives
                    }
                }                                                            
            }                                                                    
        }
    }

    [void] RemoveDeprecatedInitiatives()
    {
        if($null -ne $this.SubPolicyInitiative -and ($this.SubPolicyInitiative.DeprecatedInitiatives | Measure-Object).Count -gt 0)
        {
            $deprecatedInitiatives = @();
            $this.SubPolicyInitiative.DeprecatedInitiatives | ForEach-Object {
                $depInitiative = $_;
                $deprecatedInitiative = Get-AzPolicySetDefinition -Name $depInitiative -ErrorAction SilentlyContinue
                if($null -ne $deprecatedInitiative)
                {
                    $deprecatedInitiatives += $deprecatedInitiative;
                }                
            }
            if(($deprecatedInitiatives | Measure-Object).Count -gt 0)
            {
                $deprecatedInitiatives | ForEach-Object {
                    $depInitiative = $_;
                    $assignments = @();
                    $tassignment = Get-AzPolicyAssignment -PolicyDefinitionId $depInitiative.PolicySetDefinitionId -ErrorAction SilentlyContinue
                    if(($tassignment | Measure-Object).Count -gt 0)
                    {
                        $assignments += $tassignment
                    }
                    if(($assignments | Measure-Object).Count -gt 0)                    
                    {
                        $assignments | ForEach-Object {                        
                            $assignment = $_;
                            Remove-AzPolicyAssignment -Scope $assignment.Scope -Name $assignment.Name -Force -ErrorAction SilentlyContinue
                        }
                    }
                }

                Start-Sleep -Seconds 15
                $deprecatedInitiatives | ForEach-Object {
                    $depInitiative = $_;
                    Remove-AzPolicySetDefinition -Name $depInitiative.Name -Force -ErrorAction SilentlyContinue                    
                }
            }        
        }
    }

    [MessageData[]] RemoveARMPolicies()
    {    
        [MessageData[]] $messages = @();
        if(($this.ARMPolicyObj.Policies | Measure-Object).Count -ne 0)
        {
            if($this.GetApplicableARMPolicies() -ne 0)
            {
                $startMessage = [MessageData]::new("Processing ARM policies. Tags:[$([string]::Join(",", $this.FilterTags))]. Total policies: $($this.GetApplicableARMPolicies().Count)");
                $messages += $startMessage;
                $this.PublishCustomMessage($startMessage);
                $this.PublishCustomMessage("Note: Removing ARM policies can take few minutes depending on number of policies to be processed...", [MessageType]::Warning);                

                $disabledPolicies = $this.GetApplicableARMPolicies() | Where-Object { -not $_.Enabled };
                if(($disabledPolicies | Measure-Object).Count -ne 0)
                {
                    $disabledMessage = "Found ARM policies which are disabled and will not be removed. Total disabled policies: $($disabledPolicies.Count)";
                    $messages += [MessageData]::new($disabledMessage, $disabledPolicies);
                    $this.PublishCustomMessage($disabledMessage, [MessageType]::Warning);
                }

                $currentPolicies = @();
                $currentPolicies += Get-AzPolicyAssignment | Select-Object -Property Name | Select-Object -ExpandProperty Name
    
                $enabledPolicies = @();
                if($currentPolicies.Count -ne 0)
                {
                    $enabledPolicies += $this.GetApplicableARMPolicies() | Where-Object { $_.Enabled -and $currentPolicies -contains $_.policyDefinitionName };
                }
                
                if($enabledPolicies.Count -ne 0)
                {
                    $messages += [MessageData]::new([Constants]::SingleDashLine + "`r`nRemoving following ARM policies from the subscription. Total policies: $($enabledPolicies.Count)", $enabledPolicies);                                            

                    $errorCount = 0;
                    $currentCount = 0;
                    $enabledPolicies | ForEach-Object {
                        $policyName = $_.PolicyDefinitionName;
                        $currentCount += 1;
                        # Remove ARM policy
                        try
                        {
                            Remove-AzPolicyAssignment -Name $_.PolicyDefinitionName -Scope $_.Scope -ErrorAction Stop | Out-Null
                            Start-Sleep -Seconds 15
                            Remove-AzPolicyDefinition -Name $_.PolicyDefinitionName -Force -ErrorAction Stop | Out-Null           
                        }
                        catch
                        {
                            $messages += [MessageData]::new("Error while removing ARM policy [$policyName] from the subscription", $_, [MessageType]::Error);
                            $errorCount += 1;
                        }

                        
                    };

                    [MessageData[]] $resultMessages = @();
                    if($errorCount -eq 0)
                    {
                        #removing the version tag at AzSKRG
                        $azskRGName = [ConfigurationManager]::GetAzSKConfigData().AzSKRGName;
                        [ResourceGroupHelper]::SetResourceGroupTags($azskRGName,@{[Constants]::ARMPolicyConfigVersionTagName=$this.ARMPolicyObj.Version}, $true)
                        
                        $this.CommandProgress($enabledPolicies.Count, $currentCount, 2);
                        $resultMessages += [MessageData]::new("All ARM policies have been removed from the subscription successfully`r`n" + [Constants]::SingleDashLine, [MessageType]::Update);
                    }
                    elseif($errorCount -eq $enabledPolicies.Count)
                    {
                        $resultMessages += [MessageData]::new("No ARM policies have been removed from the subscription due to error occurred. Please remove the ARM policies manually.`r`n" + [Constants]::SingleDashLine, [MessageType]::Error);
                    }
                    else
                    {
                        $resultMessages += [MessageData]::new("$errorCount/$($enabledPolicies.Count) ARM policy(ies) have not been removed from the subscription. Please remove the ARM policies manually or contact AzSK support team.", [MessageType]::Error);
                        $resultMessages += [MessageData]::new("$($enabledPolicies.Count - $errorCount)/$($enabledPolicies.Count) ARM policy(ies) have been removed from the subscription successfully`r`n" + [Constants]::SingleDashLine, [MessageType]::Update);
                    }
                    $messages += $resultMessages;
                    $this.PublishCustomMessage($resultMessages);
                }
                else
                {
                    $noPolicyMessage = [MessageData]::new("No ARM policies have been configured by AzSK on the subscription. No ARM policies have been removed. ", [MessageType]::Warning);
                    $messages += $noPolicyMessage;
                    $this.PublishCustomMessage($noPolicyMessage);
                    $azskRGName = [ConfigurationManager]::GetAzSKConfigData().AzSKRGName;
                    [ResourceGroupHelper]::SetResourceGroupTags($azskRGName,@{[Constants]::ARMPolicyConfigVersionTagName=$this.ARMPolicyObj.Version}, $true)
                }
            }
            else
            {
                $this.PublishCustomMessage("No ARM policies have been found that matches the specified tags. Tags:[$([string]::Join(",", $this.FilterTags))].", [MessageType]::Warning);
            }
        }
        else
        {
            $this.PublishCustomMessage("No ARM policies found in the ARM policy file", [MessageType]::Warning);
        }
        return $messages;
    }    

    [void] RemoveARMPolicies([string] $PolicyPrefix, [string] $scope)
    {    
        [MessageData[]] $messages = @();

        $policies = Get-AzPolicyDefinition
        if(($policies | Measure-Object).Count -le 0)
        {
            #return if no policies are found
            return;
        }

        $filteredPolicies = $policies | Where-Object { $_.Name -like "$PolicyPrefix*" }
        if(($filteredPolicies |Measure-Object).Count -le 0)
        {
            #return if no policies are found matching the prefix
            return;
        }

        $policyAssignments = Get-AzPolicyAssignment -Scope $scope

        $filteredPolicies | ForEach-Object {
            try{
                $policy = $_;
                $subPolicyAssignments = @();
                $subPolicyAssignments += $policyAssignments | Where-Object { $_.properties.policyDefinitionId -eq $policy.PolicyDefinitionId }
                if(($subPolicyAssignments | Measure-Object).Count -gt 0)
                {
                    $subPolicyAssignments | ForEach-Object {
                        Remove-AzPolicyAssignment -Name $_.Name -Scope $_.properties.scope -ErrorAction Stop | Out-Null
                    }
                }                        
            }
            catch
            {
                $this.PublishException($_);
            }
        }

        Start-Sleep -Seconds 15
        $filteredPolicies | ForEach-Object {
            try{
                $policy = $_;
                Remove-AzPolicyDefinition -Id $policy.PolicyDefinitionId -Force -ErrorAction Stop | Out-Null
            }
            catch
            {
                $this.PublishException($_);
            }
        }
    }        

    [MessageData[]] SetPolicyInitiative()
    {    
        [MessageData[]] $messages = @();
        #$isPolicyInitiativeEnabled = [ConfigurationManager]::GetAzSKConfigData().EnableAzurePolicyBasedScan;
        $isPolicyInitiativeEnabled = [FeatureFlightingManager]::GetFeatureStatus("EnableAzurePolicyBasedScan",$($this.SubscriptionContext.SubscriptionId))
        try{
            if($isPolicyInitiativeEnabled)
        {
            $initiativeName = [ConfigurationManager]::GetAzSKConfigData().AzSKInitiativeName
            if($null -ne $this.SubPolicyInitiative)
            {
                $this.RemoveDeprecatedInitiatives();
                if($this.SubPolicyInitiative.Name -eq $initiativeName -and ($this.SubPolicyInitiative.Policies | Measure-Object).Count -gt 0)
                {        
                    $initiative = $null;
                    $initiativeAssignment = $null
                    try
                    {            
                        #check if initiative already exists
                        $initiative = Get-AzPolicySetDefinition -Name $initiativeName -ErrorAction SilentlyContinue;
                        if(($initiative|Measure-Object).Count -gt 0)
                        {
                            $initiativeAssignment = Get-AzPolicyAssignment -PolicyDefinitionId $initiative.PolicySetDefinitionId
                        }
                    }
                    catch
                    {
                        #eat this exception as error action is not working
                    }
                    $assignmentName = $initiativeName + "-assignment"
                    $assignmentDisplayName = $this.SubPolicyInitiative.DisplayName + " assignment"
                    $scope = "/subscriptions/$($this.SubscriptionContext.SubscriptionId)"
                    if($null -eq $initiative)
                    {
                        $this.PublishCustomMessage("Creating new AzSK Initiative...", [MessageType]::Update);        
                        $PolicyDefnitions = $this.SubPolicyInitiative.Policies | ConvertTo-Json -depth 10 | Out-String
                        New-AzPolicySetDefinition -Name $initiativeName -DisplayName $this.SubPolicyInitiative.DisplayName -Description $this.SubPolicyInitiative.Description -PolicyDefinition $PolicyDefnitions | Out-Null
                        Start-Sleep -Seconds 15
                        
                    }
                    elseif($this.UpdateInitiative) {
                        $this.PublishCustomMessage("Updating AzSK Initiative...", [MessageType]::Update);
                        $PolicyDefnitions = $this.SubPolicyInitiative.Policies | ConvertTo-Json -depth 10 | Out-String
                        Set-AzPolicySetDefinition -Name $this.SubPolicyInitiative.Name -DisplayName $this.SubPolicyInitiative.DisplayName -Description $this.SubPolicyInitiative.Description -PolicyDefinition $PolicyDefnitions | Out-Null
                        Start-Sleep -Seconds 15                        
                    }
                    elseif($null -ne $initiative)
                    {
                        $this.PublishCustomMessage("Found existing AzSK Initiative.", [MessageType]::Update);    
                    }    
                    if($null -eq $initiativeAssignment)
                    {
                        $setDefnObj = Get-AzPolicySetDefinition -Name $initiativeName -ErrorAction SilentlyContinue;
                        New-AzPolicyAssignment -Name $assignmentName -DisplayName $assignmentDisplayName -Scope $scope -PolicySetDefinition $setDefnObj 
                    }
                    #todo: CA permission update if default CA
                }                
            }
        }
    
        }
        catch
        {
            #eat up exception to allow this functionality to run in preview mode and not to hamper existing functionality
        }
        return $messages;
        
    }

    [string[]] ValidatePolicyConfiguration()
    {        
        $NonCompliantObjects = @();
        $enabledPolicies = $this.GetApplicableARMPolicies() | Where-Object { $_.Enabled };
        if($null -ne $this.ARMPolicyObj -and ($enabledPolicies | Measure-Object).Count -gt 0)
        {
            $RequiredPolicyDefns = @();            
            $enabledPolicies | ForEach-Object {
                $Policy = $_;
                try
                {
                    $PolicyDefn = Get-AzPolicyDefinition -Name $Policy.policyDefinitionName -ErrorAction Stop
                    if($null -ne $PolicyDefn)
                    {
                        $RequiredPolicyDefns += $PolicyDefn;
                    }
                }
                catch
                {
                    $NonCompliantObjects += ("Policy :[" + $Policy.policyDefinitionName + "]");
                    #eat this exception as silently continue is not working for this scenario
                }                
            }
            if(($RequiredPolicyDefns | Measure-Object).Count -gt 0)
            {
                $RequiredPolicyDefns | ForEach-Object {
                    $defn = $_;
                    $tassignment = Get-AzPolicyAssignment -PolicyDefinitionId $defn.policyDefinitionId  -ErrorAction SilentlyContinue
                    if($null -eq $tassignment)
                    {
                        $NonCompliantObjects += ("Policy :[" + $defn.Name + "]");
                    }
                }                                                                        
            }    
        }

        $isPolicyInitiativeEnabled = [FeatureFlightingManager]::GetFeatureStatus("EnableAzurePolicyBasedScan",$($this.SubscriptionContext.SubscriptionId))
        if($isPolicyInitiativeEnabled)
        {
            $initiativeName = [ConfigurationManager]::GetAzSKConfigData().AzSKInitiativeName        
            if($null -ne $this.SubPolicyInitiative)
            {                
                if($this.SubPolicyInitiative.Name -eq $initiativeName -and ($this.SubPolicyInitiative.Policies | Measure-Object).Count -gt 0)
                {        
                    $initiative = $null;
                    try
                    {            
                        $initiative = Get-AzPolicySetDefinition -Name $initiativeName -ErrorAction SilentlyContinue;
                    }
                    catch
                    {
                        $NonCompliantObjects += ("Policy Initiative :[" + $initiativeName + "]");
                        #eat this exception as error action is not working
                    }
                    if($null -ne $initiative)
                    {
                        $policyDefinitions = $initiative.Properties.policyDefinitions;
                        $this.SubPolicyInitiative.Policies | ForEach-Object {
                            $configuredPolicyDefn = $_;
                            if(($policyDefinitions | Where-Object { $_.policyDefinitionId -eq $configuredPolicyDefn.policyDefinitionId} | Measure-Object).Count -le 0)
                            {
                                $NonCompliantObjects += ("Policy Initiative :[" + $initiativeName + "] -> Definition :[" + $_.policyDefinitionId + "]");
                            }
                        }
                    }                
                }
            }
        }
        return ($NonCompliantObjects | Select-Object -Unique);
    }
}

class ARMPolicyModel
{
    [string] $Version
    [PSObject[]] $Policies
    [string[]] $DeprecatedPolicies
}

class PolicyInitiative 
{
    [string] $Version;
    [string] $Name;
    [string] $DisplayName;
    [string] $Description;
    [PSObject[]] $Policies;    
    [string[]] $DeprecatedInitiatives;
}