Public/Configuration/Update-EntraOpsRequiredWorkflowParameters.ps1

<#
.SYNOPSIS
    Update workflow definitions for GitHub actions with required environment values for EntraOps automation from config file.

.DESCRIPTION
    Update workflow definitions for GitHub actions with required environment values for EntraOps automation from config file.

.PARAMETER WorkflowFolderPath
    Folder where the workflow files are stored. Default is "./.github/workflows".

.PARAMETER ConfigFile
    Location of the config file which will be used to update the workflow files. Default is "./EntraOpsConfig.json".

.EXAMPLE
    Update all workflows in default location (/.github/workflows) with values from config file:
    Update-EntraOpsRequiredWorkflowParameters -ConfigFile "./EntraOps.config"
 #>


function Update-EntraOpsRequiredWorkflowParameters {

    [CmdletBinding()]
    param (
        [parameter(Mandatory = $false)]
        [ValidateScript({ Test-Path $_ })]
        [string]$WorkflowFolderPath = "./.github/workflows",

        [Parameter(Mandatory = $false)]
        [ValidateScript({ Test-Path $_ })]
        [string]$ConfigFile = "./EntraOpsConfig.json"
    )

    # Get all workflow files and content
    Write-Verbose -Message "Reading all workflow files from $WorkflowFolderPath"
    $Workflows = Get-ChildItem -Path $WorkflowFolderPath -Filter "*.yaml" -Recurse
    $Config = Get-Content -Path $ConfigFile | ConvertFrom-Json

    # Validate update policy before writing any workflow so a malformed configuration cannot leave a
    # partially materialized workflow set behind.
    $ValidationFrequency = [string]$Config.AutomatedEntraOpsUpdate.ValidationFrequency
    if ([string]::IsNullOrWhiteSpace($ValidationFrequency)) {
        $ValidationFrequency = 'OnChange'
    }
    if ($ValidationFrequency -notin @('OnChange', 'Always', 'Never')) {
        throw "Unsupported AutomatedEntraOpsUpdate.ValidationFrequency '$ValidationFrequency'. Use OnChange, Always, or Never."
    }
    $ConfiguredRunBrowserTests = $Config.AutomatedEntraOpsUpdate.RunBrowserTests
    $RunBrowserTests = if ($null -eq $ConfiguredRunBrowserTests) {
        $true
    } elseif ($ConfiguredRunBrowserTests -isnot [bool]) {
        throw 'AutomatedEntraOpsUpdate.RunBrowserTests must be a JSON boolean (true or false).'
    } else {
        $ConfiguredRunBrowserTests
    }
    $PublicationMode = [string]$Config.AutomatedEntraOpsUpdate.PublicationMode
    if ([string]::IsNullOrWhiteSpace($PublicationMode)) {
        $PublicationMode = 'PullRequest'
    }
    if ($PublicationMode -notin @('PullRequest', 'DirectPush')) {
        throw "Unsupported AutomatedEntraOpsUpdate.PublicationMode '$PublicationMode'. Use PullRequest or DirectPush."
    }

    # Only the deployment workflows carry tenant parameters. CI workflows such as Test-EntraOps.yaml
    # have no "env" block and must not be rewritten with tenant values.
    $DeploymentWorkflowNames = @(
        "Pull-EntraOpsPrivilegedEAM.yaml"
        "Pull-EntraOpsTenantGovernance.yaml"
        "Push-EntraOpsPrivilegedEAM.yaml"
        "Push-EntraOpsPrivilegedReporting.yaml"
        "Update-EntraOps.yaml"
    )
    $DeploymentWorkflows = @($Workflows | Where-Object { $_.Name -in $DeploymentWorkflowNames })
    $MissingWorkflowNames = @($DeploymentWorkflowNames | Where-Object { $_ -notin $DeploymentWorkflows.Name })
    if ($MissingWorkflowNames.Count -gt 0) {
        Write-Warning "The following expected EntraOps workflow file(s) were not found in $($WorkflowFolderPath): $($MissingWorkflowNames -join ', '). Their parameters cannot be updated."
    }

    # powershell-yaml does not preserve comments. Capture immutable action version annotations once
    # and restore them after every serialization so configuration updates do not invalidate the
    # repository's GitHub Action reference policy.
    $ActionVersionByReference = @{}
    foreach ($Workflow in $Workflows) {
        $WorkflowText = Get-Content -LiteralPath $Workflow.FullName -Raw
        foreach ($Match in [regex]::Matches(
                $WorkflowText,
                '(?m)^\s*(?:-\s*)?uses:\s*(?<Reference>[^\s#]+)\s+#\s*(?<Version>v\d+(?:\.\d+){0,2})\s*$')) {
            $ActionVersionByReference[$Match.Groups['Reference'].Value] = $Match.Groups['Version'].Value
        }
    }

    function ConvertTo-EntraOpsWorkflowYaml {
        param (
            [Parameter(Mandatory = $true)]
            [object]$WorkflowObject
        )

        $Yaml = ($WorkflowObject | ConvertTo-Yaml).Replace('"on"', 'on')
        foreach ($Reference in $ActionVersionByReference.Keys) {
            $Pattern = '(?m)^(?<Use>\s*(?:-\s*)?uses:\s*' + [regex]::Escape($Reference) + ')\s*$'
            $Replacement = '${Use} # ' + $ActionVersionByReference[$Reference]
            $Yaml = [regex]::Replace($Yaml, $Pattern, $Replacement)
        }
        return $Yaml
    }

    # Check if required module is available
    Write-Verbose -Message "Checking if powershell-yaml module is available"
    Install-EntraOpsRequiredModule -ModuleName powershell-yaml

    #region Update all Workflows with default values
    Write-Verbose -Message "Updating all workflows with default values"
    foreach ($Workflow in $DeploymentWorkflows) {
        try {
            $WorkflowContent = Get-Content -Path $workflow.FullName | ConvertFrom-Yaml -Ordered

            # Set Parameters
            $WorkflowContent.env.ClientId = $Config.ClientId
            $WorkflowContent.env.AuthenticationType = $Config.AuthenticationType
            $WorkflowContent.env.TenantId = $Config.TenantId
            $WorkflowContent.env.TenantName = $Config.TenantName
            $WorkflowContent.env.ConfigFile = $ConfigFile
            $UpdatedWorkflowContent = ConvertTo-EntraOpsWorkflowYaml -WorkflowObject $WorkflowContent
            $UpdatedWorkflowContent | Set-Content -Path $workflow.FullName
        } catch {
            Write-Error "Failed to update workflow $($workflow.FullName). Error: $_"
        }

    }
    #endregion

    #region Set specific parameters for push pipeline
    Write-Verbose -Message "Updating specific parameters for push pipeline"
    $PushWorkflow = $Workflows | Where-Object { $_.Name -eq "Push-EntraOpsPrivilegedEAM.yaml" }
    $PushWorkflowObject = Get-Content -Path $PushWorkflow.FullName | ConvertFrom-Yaml -Ordered

    # Always persist these values, including $false, so the workflow template never overrides config.
    $PushWorkflowObject.env.IngestToLogAnalytics = [bool]$Config.LogAnalytics.IngestToLogAnalytics
    $PushWorkflowObject.env.IngestToWatchLists = [bool]$Config.SentinelWatchLists.IngestToWatchLists
    $PushWorkflowObject.env.ApplyAdministrativeUnitAssignments = [bool]$Config.AutomatedAdministrativeUnitManagement.ApplyAdministrativeUnitAssignments
    $PushWorkflowObject.env.ApplyRmauAssignmentsForUnprotectedObjects = [bool]$Config.AutomatedRmauAssignmentsForUnprotectedObjects.ApplyRmauAssignmentsForUnprotectedObjects

    $PushWorkflowObject.env.ApplyPrivilegedElmCatalogProtection = [bool]$Config.AutomatedElmCatalogProtection.ApplyPrivilegedElmCatalogProtection
    
    $PushWorkflowObject.env.ApplyConditionalAccessTargetGroups = [bool]$Config.AutomatedConditionalAccessTargetGroups.ApplyConditionalAccessTargetGroups

    # Reconcile workflow_run so disabling and later re-enabling the trigger are both supported.
    if ($Config.WorkflowTrigger.PushAfterPullWorkflowTrigger -ne $false) {
        $PushWorkflowObject['on']['workflow_run'] = [ordered]@{
            workflows = @('Pull-EntraOpsPrivilegedEAM')
            types     = @('completed')
        }
    } elseif ($PushWorkflowObject['on'].Contains('workflow_run')) {
        $PushWorkflowObject.on.Remove('workflow_run')
    }

    # Save settings to workflow
    $UpdatedPushWorkflowContent = ConvertTo-EntraOpsWorkflowYaml -WorkflowObject $PushWorkflowObject
    $UpdatedPushWorkflowContent | Set-Content -Path $PushWorkflow.FullName
    #endregion

    #region Set specific parameters for reporting pipeline
    Write-Verbose -Message "Updating specific parameters for reporting pipeline"
    $ReportingWorkflow = $Workflows | Where-Object { $_.Name -eq "Push-EntraOpsPrivilegedReporting.yaml" }
    if ($ReportingWorkflow) {
        $ReportingWorkflowObject = Get-Content -Path $ReportingWorkflow.FullName | ConvertFrom-Yaml -Ordered

        # Always persist master/publication switches, including $false. Older configs did not expose
        # release publishing; keep that safely disabled until the operator opts in explicitly.
        $ReportingWorkflowObject.env.ApplyAutomatedReportingGeneration = [bool]$Config.AutomatedReportingGeneration.ApplyAutomatedReportingGeneration
        $ReportingWorkflowObject.env.PublishReportsAsRelease = if ($null -ne $Config.AutomatedReportingGeneration.PublishReportsAsRelease) {
            [bool]$Config.AutomatedReportingGeneration.PublishReportsAsRelease
        } else {
            $false
        }
        # An explicitly configured empty value keeps the workflow's "no pruning" mode; only a missing value falls back.
        $ReportingWorkflowObject.env.ReportingReleasesToKeep = if ($null -eq $Config.AutomatedReportingGeneration.ReportingReleasesToKeep) {
            '10'
        } else {
            [string]$Config.AutomatedReportingGeneration.ReportingReleasesToKeep
        }

        if ($null -ne $Config.AutomatedReportingGeneration.GenerateClassificationExplorer) {
            # Set GenerateClassificationExplorer Parameter
            $ReportingWorkflowObject.env.GenerateClassificationExplorer = $Config.AutomatedReportingGeneration.GenerateClassificationExplorer
        }

        if ($null -ne $Config.AutomatedReportingGeneration.GenerateTierBreachAnalyzer) {
            # Set GenerateTierBreachAnalyzer Parameter
            $ReportingWorkflowObject.env.GenerateTierBreachAnalyzer = $Config.AutomatedReportingGeneration.GenerateTierBreachAnalyzer
        }

        if ($null -ne $Config.AutomatedReportingGeneration.GenerateEamDashboard) {
            # Set GenerateEamDashboard Parameter
            $ReportingWorkflowObject.env.GenerateEamDashboard = $Config.AutomatedReportingGeneration.GenerateEamDashboard
        }

        if ($null -ne $Config.AutomatedReportingGeneration.GenerateAccessPathMap) {
            # Set GenerateAccessPathMap Parameter
            $ReportingWorkflowObject.env.GenerateAccessPathMap = $Config.AutomatedReportingGeneration.GenerateAccessPathMap
        }

        if ($null -ne $Config.AutomatedReportingGeneration.GenerateConfigurationAnalyzer) {
            # Set GenerateConfigurationAnalyzer Parameter
            $ReportingWorkflowObject.env.GenerateConfigurationAnalyzer = $Config.AutomatedReportingGeneration.GenerateConfigurationAnalyzer
        }

        if ($null -ne $Config.AutomatedReportingGeneration.GenerateAccessPackageFlow) {
            # Set GenerateAccessPackageFlow Parameter
            $ReportingWorkflowObject.env.GenerateAccessPackageFlow = $Config.AutomatedReportingGeneration.GenerateAccessPackageFlow
        }

        if ($null -ne $Config.AutomatedReportingGeneration.GeneratePrivilegeHistory) {
            # Set GeneratePrivilegeHistory Parameter
            $ReportingWorkflowObject.env.GeneratePrivilegeHistory = $Config.AutomatedReportingGeneration.GeneratePrivilegeHistory
        }

        $PrivilegeHistoryEnabled = if ($null -ne $Config.PrivilegeHistory.EnablePrivilegeHistory) {
            [bool]$Config.PrivilegeHistory.EnablePrivilegeHistory
        } else {
            $true
        }
        $ReportingWorkflowObject.env.EnablePrivilegeHistory = $PrivilegeHistoryEnabled
        $ReportingWorkflowObject.env.PrivilegeHistoryTimeRangeInDays = if ($null -ne $Config.PrivilegeHistory.TimeRangeInDays) {
            [string]$Config.PrivilegeHistory.TimeRangeInDays
        } else {
            ''
        }
        $ReportingWorkflowObject.env.PrivilegeHistorySnapshotInterval = if (-not [string]::IsNullOrWhiteSpace($Config.PrivilegeHistory.SnapshotInterval)) {
            [string]$Config.PrivilegeHistory.SnapshotInterval
        } else {
            'P2W'
        }

        if ($null -ne $Config.AccessPathMap.ResolveObjectIdsOutsidePrivilegedEAM) {
            # Set AccessPathMapResolveObjectIdsOutsidePrivilegedEAM Parameter
            $ReportingWorkflowObject.env.AccessPathMapResolveObjectIdsOutsidePrivilegedEAM = $Config.AccessPathMap.ResolveObjectIdsOutsidePrivilegedEAM
        }

        $AllowPartialTenantGovernanceSnapshot = if ($null -ne $Config.ConfigurationAnalyzer.AllowPartialTenantGovernanceSnapshot) {
            [bool]$Config.ConfigurationAnalyzer.AllowPartialTenantGovernanceSnapshot
        } else {
            $true
        }
        $ReportingWorkflowObject.env.AllowPartialTenantGovernanceSnapshot = $AllowPartialTenantGovernanceSnapshot
        $ReportingWorkflowObject.on.workflow_dispatch.inputs.allowPartialTenantGovernance.default = $AllowPartialTenantGovernanceSnapshot

        if ($Config.AutomatedReportingGeneration.ClassificationExplorerRepository) {
            # Set ClassificationExplorerRepository Parameter
            $ReportingWorkflowObject.env.ClassificationExplorerRepository = $Config.AutomatedReportingGeneration.ClassificationExplorerRepository
        }

        # Only checkout depth depends on this workflow value; the generator reads the same positive
        # config setting directly and keeps its explicit -SkipHistory switch as an invocation override.
        $ReportingWorkflowObject.env.ClassificationExplorerGenerateChangeHistory = if ($null -ne $Config.ClassificationExplorer.GenerateChangeHistory) {
            [bool]$Config.ClassificationExplorer.GenerateChangeHistory
        } else {
            $false
        }

        # Reconcile workflow_run so disabling and later re-enabling the trigger are both supported.
        if ($Config.WorkflowTrigger.PushReportingAfterPullWorkflowTrigger -ne $false) {
            $ReportingWorkflowObject['on']['workflow_run'] = [ordered]@{
                workflows = @('Pull-EntraOpsPrivilegedEAM')
                types     = @('completed')
            }
        } elseif ($ReportingWorkflowObject['on'].Contains('workflow_run')) {
            $ReportingWorkflowObject.on.Remove('workflow_run')
        }

        # Reconcile the schedule trigger on the parsed object. String replacement of the placeholder is
        # one-way: once the schedule block is removed, re-enabling the trigger could never restore it.
        $DefaultReportingSchedule = $Config.WorkflowTrigger.PushReportingScheduledCron # By default weekly, every Monday at 09:00 UTC
        if ($Config.WorkflowTrigger.PushReportingScheduledTrigger -eq $true) {
            $ReportingWorkflowObject['on']['schedule'] = @([ordered]@{ cron = $DefaultReportingSchedule })
        } elseif ($ReportingWorkflowObject['on'].Contains('schedule')) {
            $ReportingWorkflowObject['on'].Remove('schedule')
        }

        # Save settings to workflow
        $UpdatedReportingWorkflowContent = ConvertTo-EntraOpsWorkflowYaml -WorkflowObject $ReportingWorkflowObject
        $UpdatedReportingWorkflowContent | Set-Content -Path $ReportingWorkflow.FullName
    }
    #endregion

    #region Set specific parameters for pull pipeline
    Write-Verbose -Message "Updating specific parameters for pull pipeline"
    $PullWorkflow = $Workflows | Where-Object { $_.Name -eq "Pull-EntraOpsPrivilegedEAM.yaml" }
    $PullWorkflowObject = Get-Content -Path $PullWorkflow.FullName | ConvertFrom-Yaml -Ordered

    if ($Config.AutomatedControlPlaneScopeUpdate.ApplyAutomatedControlPlaneScopeUpdate) {
        # Set ApplyAutomatedControlPlaneScopeUpdate Parameter
        $PullWorkflowObject.env.ApplyAutomatedControlPlaneScopeUpdate = $Config.AutomatedControlPlaneScopeUpdate.ApplyAutomatedControlPlaneScopeUpdate
    } else {
        $PullWorkflowObject.env.ApplyAutomatedControlPlaneScopeUpdate = $false
    }

    if ($Config.AutomatedClassificationUpdate.ApplyAutomatedClassificationUpdate) {
        # Set ApplyAutomatedClassificationUpdate Parameter
        $PullWorkflowObject.env.ApplyAutomatedClassificationUpdate = $Config.AutomatedClassificationUpdate.ApplyAutomatedClassificationUpdate
    } else {
        $PullWorkflowObject.env.ApplyAutomatedClassificationUpdate = $false
    }

    if ($Config.GeneratedArtifactValidation.FailOnContradictoryTierPair) {
        $PullWorkflowObject.env.FailOnContradictoryTierPair = $Config.GeneratedArtifactValidation.FailOnContradictoryTierPair
    } else {
        $PullWorkflowObject.env.FailOnContradictoryTierPair = $false
    }
    if ($Config.GeneratedArtifactValidation.FailOnPrivilegedAssignmentWithoutClassification) {
        $PullWorkflowObject.env.FailOnPrivilegedAssignmentWithoutClassification = $Config.GeneratedArtifactValidation.FailOnPrivilegedAssignmentWithoutClassification
    } else {
        $PullWorkflowObject.env.FailOnPrivilegedAssignmentWithoutClassification = $false
    }

    # Reconcile the schedule trigger on the parsed object so it can be disabled and re-enabled.
    $DefaultPullSchedule = $config.WorkflowTrigger.PullScheduledCron # By default every day at 10:00 UTC
    if ($config.WorkflowTrigger.PullScheduledTrigger -eq $true) {
        $PullWorkflowObject['on']['schedule'] = @([ordered]@{ cron = $DefaultPullSchedule })
    } elseif ($PullWorkflowObject['on'].Contains('schedule')) {
        $PullWorkflowObject['on'].Remove('schedule')
    }

    # Save settings to workflow
    $UpdatedPullWorkflowContent = ConvertTo-EntraOpsWorkflowYaml -WorkflowObject $PullWorkflowObject
    $UpdatedPullWorkflowContent | Set-Content -Path $PullWorkflow.FullName
    #endregion

    #region Set specific parameters for update pipeline
    Write-Verbose -Message "Updating specific parameters for update pipeline"
    $UpdateWorkflow = $Workflows | Where-Object { $_.Name -eq "Update-EntraOps.yaml" }
    $UpdateWorkflowObject = Get-Content -Path $UpdateWorkflow.FullName | ConvertFrom-Yaml -Ordered

    if ($Config.AutomatedEntraOpsUpdate.ApplyAutomatedEntraOpsUpdate) {
        # Set ApplyAutomatedEntraOpsUpdate Parameter
        $UpdateWorkflowObject.env.ApplyAutomatedEntraOpsUpdate = $Config.AutomatedEntraOpsUpdate.ApplyAutomatedEntraOpsUpdate
    } else {
        $UpdateWorkflowObject.env.ApplyAutomatedEntraOpsUpdate = $false
    }

    $UpdateWorkflowObject.env.ValidationFrequency = $ValidationFrequency
    $UpdateWorkflowObject.env.RunBrowserTests = $RunBrowserTests
    $UpdateWorkflowObject.env.PublicationMode = $PublicationMode
    if ($Config.AutomatedEntraOpsUpdate.ApplyAutomatedEntraOpsUpdate -eq $true -and $PublicationMode -eq 'PullRequest') {
        Write-Warning "AutomatedEntraOpsUpdate publishes through pull requests. Enable 'Allow GitHub Actions to create and approve pull requests' under Settings > Actions > General > Workflow permissions of the repository (or organization), otherwise the Update-EntraOps workflow cannot open the review pull request. See https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#preventing-github-actions-from-creating-or-approving-pull-requests"
    }
    if ($Config.AutomatedEntraOpsUpdate.ApplyAutomatedEntraOpsUpdate -eq $true -and
        @($Config.AutomatedEntraOpsUpdate.TargetUpdateFolders) -contains './.github/workflows') {
        Write-Warning "Automated workflow-template updates require a dedicated GitHub App installed on this repository with Contents, Workflows, Pull requests, and Commit statuses write permissions. Configure its client ID as the EntraOpsUpdateAppClientId repository variable and its private key as the EntraOpsUpdateAppPrivateKey repository secret. GITHUB_TOKEN cannot publish .github/workflows changes."
    }
    if ($Config.AutomatedEntraOpsUpdate.ApplyAutomatedEntraOpsUpdate -eq $true -and [string]$Config.AutomatedEntraOpsUpdate.Repository -eq 'EntraOps-Insiders') {
        Write-Warning "AutomatedEntraOpsUpdate.Repository is the private 'EntraOps-Insiders' channel. The Update-EntraOps workflow requires the 'EntraOpsUpdatePat' repository secret to clone it; the public 'EntraOps' release repository needs no secret."
    }

    # Reconcile the schedule trigger on the parsed object so it can be disabled and re-enabled.
    $DefaultUpdateSchedule = $config.AutomatedEntraOpsUpdate.UpdateScheduledCron # By default Wednesday at 9:00 UTC
    if ($config.AutomatedEntraOpsUpdate.UpdateScheduledTrigger -eq $true) {
        $UpdateWorkflowObject['on']['schedule'] = @([ordered]@{ cron = $DefaultUpdateSchedule })
    } elseif ($UpdateWorkflowObject['on'].Contains('schedule')) {
        $UpdateWorkflowObject['on'].Remove('schedule')
    }

    # Save settings to workflow
    $UpdatedUpdateWorkflowContent = ConvertTo-EntraOpsWorkflowYaml -WorkflowObject $UpdateWorkflowObject
    $UpdatedUpdateWorkflowContent | Set-Content -Path $UpdateWorkflow.FullName
    #endregion

    #region Set specific parameters for tenant governance pull pipeline
    Write-Verbose -Message "Updating specific parameters for tenant governance pull pipeline"
    $TenantGovernanceWorkflow = $Workflows | Where-Object { $_.Name -eq "Pull-EntraOpsTenantGovernance.yaml" }
    if ($TenantGovernanceWorkflow) {
        $TenantGovernanceWorkflowObject = Get-Content -Path $TenantGovernanceWorkflow.FullName | ConvertFrom-Yaml -Ordered

        if ($null -ne $Config.TenantGovernanceSnapshot.EnableTenantGovernanceSnapshot) {
            # Set EnableTenantGovernanceSnapshot Parameter
            $TenantGovernanceWorkflowObject.env.EnableTenantGovernanceSnapshot = $Config.TenantGovernanceSnapshot.EnableTenantGovernanceSnapshot
        } else {
            $TenantGovernanceWorkflowObject.env.EnableTenantGovernanceSnapshot = $false
        }

        # Set the start and one-shot collection schedules. SnapshotScheduledCronComplete is retained
        # as the primary collector setting for compatibility with existing configuration files.
        $DefaultTenantGovernanceStartSchedule = $Config.TenantGovernanceSnapshot.SnapshotScheduledCron # By default daily at 06:00 UTC
        $DefaultTenantGovernanceCompleteSchedule = if ($Config.TenantGovernanceSnapshot.SnapshotScheduledCronComplete) { $Config.TenantGovernanceSnapshot.SnapshotScheduledCronComplete } else { "0 7 * * *" } # By default daily at 07:00 UTC
        $DefaultTenantGovernanceCompleteRetry1Schedule = if ($Config.TenantGovernanceSnapshot.SnapshotScheduledCronCompleteRetry1) { $Config.TenantGovernanceSnapshot.SnapshotScheduledCronCompleteRetry1 } else { "30 7 * * *" }
        $DefaultTenantGovernanceCompleteRetry2Schedule = if ($Config.TenantGovernanceSnapshot.SnapshotScheduledCronCompleteRetry2) { $Config.TenantGovernanceSnapshot.SnapshotScheduledCronCompleteRetry2 } else { "0 8 * * *" }

        $TenantGovernanceWorkflowObject.env.SnapshotStartCron = $DefaultTenantGovernanceStartSchedule
        if ($Config.TenantGovernanceSnapshot.EnableTenantGovernanceSnapshot -eq $true -and $Config.TenantGovernanceSnapshot.SnapshotScheduledTrigger -eq $true) {
            $TenantGovernanceWorkflowObject['on'].schedule = @(
                [ordered]@{ cron = $DefaultTenantGovernanceStartSchedule }
                [ordered]@{ cron = $DefaultTenantGovernanceCompleteSchedule }
                [ordered]@{ cron = $DefaultTenantGovernanceCompleteRetry1Schedule }
                [ordered]@{ cron = $DefaultTenantGovernanceCompleteRetry2Schedule }
            )
        } else {
            $TenantGovernanceWorkflowObject['on'].Remove('schedule')
        }

        # Save settings to workflow
        $UpdatedTenantGovernanceWorkflowContent = ConvertTo-EntraOpsWorkflowYaml -WorkflowObject $TenantGovernanceWorkflowObject
        $UpdatedTenantGovernanceWorkflowContent | Set-Content -Path $TenantGovernanceWorkflow.FullName
    }
    #endregion
}

# SIG # Begin signature block
# MIIniAYJKoZIhvcNAQcCoIIneTCCJ3UCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDtEV8AinMMswap
# rA+HYgRHDZv/EYIGmdabS+/uQlQfpKCCIDwwggXJMIIEsaADAgECAhAbtY8lKt8j
# AEkoya49fu0nMA0GCSqGSIb3DQEBDAUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK
# ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2Vy
# dGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5l
# dHdvcmsgQ0EwHhcNMjEwNTMxMDY0MzA2WhcNMjkwOTE3MDY0MzA2WjCBgDELMAkG
# A1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAl
# BgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMb
# Q2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMIICIjANBgkqhkiG9w0BAQEFAAOC
# Ag8AMIICCgKCAgEAvfl4+ObVgAxknYYblmRnPyI6HnUBfe/7XGeMycxca6mR5rlC
# 5SBLm9qbe7mZXdmbgEvXhEArJ9PoujC7Pgkap0mV7ytAJMKXx6fumyXvqAoAl4Va
# qp3cKcniNQfrcE1K1sGzVrihQTib0fsxf4/gX+GxPw+OFklg1waNGPmqJhCrKtPQ
# 0WeNG0a+RzDVLnLRxWPa52N5RH5LYySJhi40PylMUosqp8DikSiJucBb+R3Z5yet
# /5oCl8HGUJKbAiy9qbk0WQq/hEr/3/6zn+vZnuCYI+yma3cWKtvMrTscpIfcRnNe
# GWJoRVfkkIJCu0LW8GHgwaM9ZqNd9BjuiMmNF0UpmTJ1AjHuKSbIawLmtWJFfzcV
# WiNoidQ+3k4nsPBADLxNF8tNorMe0AZa3faTz1d1mfX6hhpneLO/lv403L3nUlbl
# s+V1e9dBkQXcXWnjlQ1DufyDljmVe2yAWk8TcsbXfSl6RLpSpCrVQUYJIP4ioLZb
# MI28iQzV13D4h1L92u+sUS4Hs07+0AnacO+Y+lbmbdu1V0vc5SwlFcieLnhO+Nqc
# noYsylfzGuXIkosagpZ6w7xQEmnYDlpGizrrJvojybawgb5CAKT41v4wLsfSRvbl
# jnX98sy50IdbzAYQYLuDNbdeZ95H7JlI8aShFf6tjGKOOVVPORa5sWOd/7cCAwEA
# AaOCAT4wggE6MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLahVDkCw6A/joq8
# +tT4HKbROg79MB8GA1UdIwQYMBaAFAh2zcsH/yT2xc3tu5C84oQ3RnX3MA4GA1Ud
# DwEB/wQEAwIBBjAvBgNVHR8EKDAmMCSgIqAghh5odHRwOi8vY3JsLmNlcnR1bS5w
# bC9jdG5jYS5jcmwwawYIKwYBBQUHAQEEXzBdMCgGCCsGAQUFBzABhhxodHRwOi8v
# c3ViY2Eub2NzcC1jZXJ0dW0uY29tMDEGCCsGAQUFBzAChiVodHRwOi8vcmVwb3Np
# dG9yeS5jZXJ0dW0ucGwvY3RuY2EuY2VyMDkGA1UdIAQyMDAwLgYEVR0gADAmMCQG
# CCsGAQUFBwIBFhhodHRwOi8vd3d3LmNlcnR1bS5wbC9DUFMwDQYJKoZIhvcNAQEM
# BQADggEBAFHCoVgWIhCL/IYx1MIy01z4S6Ivaj5N+KsIHu3V6PrnCA3st8YeDrJ1
# BXqxC/rXdGoABh+kzqrya33YEcARCNQOTWHFOqj6seHjmOriY/1B9ZN9DbxdkjuR
# mmW60F9MvkyNaAMQFtXx0ASKhTP5N+dbLiZpQjy6zbzUeulNndrnQ/tjUoCFBMQl
# lVXwfqefAcVbKPjgzoZwpic7Ofs4LphTZSJ1Ldf23SIikZbr3WjtP6MZl9M7JYjs
# NhI9qX7OAo0FmpKnJ25FspxihjcNpDOO16hO0EoXQ0zF8ads0h5YbBRRfopUofbv
# n3l6XYGaFpAP4bvxSgD5+d2+7arszgowggZrMIIEU6ADAgECAhBX5gfP8LabvnxC
# XoItEBa/MA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhB
# c3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBDb2RlIFNp
# Z25pbmcgMjAyMSBDQTAeFw0yNjEwMDYwODI4MjNaFw0yNzEwMDYwODI4MjJaMIGQ
# MQswCQYDVQQGEwJERTEdMBsGA1UECAwUUmhpbmVsYW5kLVBhbGF0aW5hdGUxEjAQ
# BgNVBAcMCUxhaG5zdGVpbjEeMBwGA1UECgwVT3BlbiBTb3VyY2UgRGV2ZWxvcGVy
# MS4wLAYDVQQDDCVPcGVuIFNvdXJjZSBEZXZlbG9wZXIgVGhvbWFzIE5hdW5oZWlt
# MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAphp2ljSmVsRB6gvkEq7A
# DUMENpKZiDFoAfF1ktD1nCwaVyI+bJ0ROjHwAn4iJgS+vlBDPOUIw2GLHkiqUhOg
# HSwzg6gj45GNmt6zxsKP0zPlhMB/fb0zmz7/F7e3yfKCXLfhbHuk0Es9gJ+8Wmet
# 9StVwwSE/K0MpW06GKA9nPWyE4N/ADwigDIj9rsTKJR4cve9w8G1b3zohpUc/qMr
# onab+HVw5r9b3sOBnnaXC6zvrX4fovRz1ztwfWjRjILLoFG6mW/TuK5F2ICD3Rhl
# 9R2rJWWWhzTDMsQlzvX+/SaM74q3OFpIVNdhvtmWU9pKUBRAyYMijcDqKEFGSzrc
# 8JFmGMpP5TuroWF+N21d3px2eARvW1mV0DCfFAH+Og20SH/gVW6/ltQQEkH/z/Zt
# 49ARyJrgESLOQtlMCIYSoKISdkk4ml4VUS1E/PcbzXEYj3a7m6DOxcd3WuY03UT9
# cnk3p0LMN6LeLYAZgVY+HRloZlH+kW2SkOHNuWgtXvZhAgMBAAGjggF4MIIBdDAM
# BgNVHRMBAf8EAjAAMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly9jY3NjYTIwMjEu
# Y3JsLmNlcnR1bS5wbC9jY3NjYTIwMjEuY3JsMHMGCCsGAQUFBwEBBGcwZTAsBggr
# BgEFBQcwAYYgaHR0cDovL2Njc2NhMjAyMS5vY3NwLWNlcnR1bS5jb20wNQYIKwYB
# BQUHMAKGKWh0dHA6Ly9yZXBvc2l0b3J5LmNlcnR1bS5wbC9jY3NjYTIwMjEuY2Vy
# MB8GA1UdIwQYMBaAFN10XUwA23ufoHTKsW73PMAywHDNMB0GA1UdDgQWBBTBvGyH
# MXj1dMbVnxuof9oP4PY7cDBLBgNVHSAERDBCMAgGBmeBDAEEATA2BgsqhGgBhvZ3
# AgUBBDAnMCUGCCsGAQUFBwIBFhlodHRwczovL3d3dy5jZXJ0dW0ucGwvQ1BTMBMG
# A1UdJQQMMAoGCCsGAQUFBwMDMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsF
# AAOCAgEAkC165c7/r6y3VeEEpNXas9bQs6ZpKPdJffHkf9xoPi4kWvAIWGc4ocG1
# DJLb18RXiDYShDmT5sb/6I08FeipKi2DNJkuOq6yOTJ3S7+hNFIZCyd6i7WKO8ea
# P5I+FAbyJP4+5coZoMg45bUa9JoocaJCm+5n7WiCfN7/EQpClW5Trm4+ZMbp7EMN
# F1uLJgGCMnN0SsoUarp7PHlqupMxMC4Uhhe+lAP2ytcSIU81L97/qjrADbLWqICk
# 9il5iGM+aZndguq/cmfH9rJj4GRZHWnG6WwnzYD9+KgYlRI6em+/9dq+IRiXe2Bs
# thUuIaFW7U2gWw8tBTEYm1UBLHdJx+L1gjJCLs5KT/wy5wU32wZxNDqPUQejUC1x
# GvU3cKqmLgRq+yNf3gFVfzqIoggyM3U9wQvnk7nBtCkpjXS2hdx4yxzcmrlWsDHr
# z6+6/jeyQySzbYUlFmKnc4n9Mw2soHOajEua5poFARVRS5KgUBuiiQ21YFNYzWBo
# qGuECaaAI/C5rGxapMJ3dAOBnHw6NLqBet+ZXBJEccX5y89MLlOQu+jDmJP8HieR
# qDLlwfhVZnw4RY8Rus43NZBwhNlVuOUXqocHKmJhKNlekda3qsJj93eQBQOxKJcc
# w5O08q9jukpH2+yzFAj+7ll33DuNUgWptogc4Awtbx3glzcEvJQwggaCMIIEaqAD
# AgECAhAo8HfBHDa9/l90MkdwJy4DMA0GCSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYT
# AlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMT
# G0NlcnR1bSBUaW1lc3RhbXBpbmcgMjAyMSBDQTAeFw0yNjAzMTEwNzM0NTRaFw0z
# NjAyMjcwNzM0NTRaMFAxCzAJBgNVBAYTAlBMMSEwHwYDVQQKDBhBc3NlY28gRGF0
# YSBTeXN0ZW1zIFMuQS4xHjAcBgNVBAMMFUNlcnR1bSBUaW1lc3RhbXAgMjAyNjCC
# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALi9zfBw+xI12hi2F9uNQBNO
# STkTIi13fuh3DiWybI5392bBfJw6tO7zDs7UqP/rejVf4xFTU3XNOPsuOFkP5SPF
# snWwn3VJL+NhNhsuujvYsQtaQnvNo1wk46reTgws1L8OFEdKaxgyqT4zOQx81mLw
# NZSqXNU9Rrb9oAJWJF4Mydz+C1ebu0D0+vgM3tqAQ8TU203fqgKCPqIdHloaFqH9
# eyR+8tnpmy2hnnN59ZOtmsuHeRwf5iTOhlhf0d/qqquLOSIGPgGVL1Hj5XBUox2O
# E7tQRr+l/xxNbL6OuwBO4Aa2EbuxYg37cpXv5i2/lTL4NASB/duf9W4fjA3Ro+GP
# +bC54n2OYiM3Cq8z92ejx8F5HWO2e4/75eC95B9tMw/eq7109GDyW2+2ut5Bn0o8
# MXJTERkyystWbqcnUkomA97qfiDCwvpWuOMdfTOksXe9S+xDxOJaCbiyTivgPulx
# cT+tfQTRXBr5UwtPvc+INnafOEilzVHA11tv+pHcylryxBc08cUihf/ttqkZS0bm
# x3VxQay8uohaTp0//ECA9vYGW51SKdJMJhXvKyIdBLy8WYNchTt8yRITv9Adxg9J
# qGujh+TnP1bW3aQjFtlEl7SzgNeI8OVA/CJAWWUuRT3Au9ToOpHy/flrrknhN4he
# Ew7/6ILMv4HSsj0YZ4otAgMBAAGjggFQMIIBTDB1BggrBgEFBQcBAQRpMGcwOwYI
# KwYBBQUHMAKGL2h0dHA6Ly9zdWJjYS5yZXBvc2l0b3J5LmNlcnR1bS5wbC9jdHNj
# YTIwMjEuY2VyMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1jZXJ0dW0u
# Y29tMB8GA1UdIwQYMBaAFL5UAi+/QGxzQ86sCSVOnkNEGu7gMAwGA1UdEwEB/wQC
# MAAwOQYDVR0fBDIwMDAuoCygKoYoaHR0cDovL3N1YmNhLmNybC5jZXJ0dW0ucGwv
# Y3RzY2EyMDIxLmNybDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8E
# BAMCB4AwIgYDVR0gBBswGTAIBgZngQwBBAIwDQYLKoRoAYb2dwIFAQswHQYDVR0O
# BBYEFCM5aiirmhKnpKfyH/lFmr+N/oIDMA0GCSqGSIb3DQEBDAUAA4ICAQBm/pOb
# rAveTqYFPCSthxNVwBHTW4TfuBQY2KGCdMlH6ALjVPYf2XARNGMTXScR0JQ2/c2L
# xLpoHHhRwxFf3d933DhFS165rYj6iPSQKm41rc9AMpTCjMdmai1aK9o870pOzTzL
# RhWFVIxm+5Qf+t4V5z14tPoUsAWdfRKD1Zzvzfe5AHlE9rxOZcGROiULxs3Edf7Z
# J8RZI0jzD9juj2oEX2XS7BVKuNCK1W0h1mjapv028yqo8UtZh5/1Ib48Yyp88BDY
# Shh5PSGUUuS5tYXVfcduIPBe3PdNTYRYdT5VNxkwLgjMzIcr/6SH2fejFJDjnCxj
# vXLuarErJg/IdI+hXdGIBDVOGv1zrkF4ktoQvFZIp5DIAYh0GwyUhc87qqn5UaYT
# j2+iyiBDSNh7aGCrkuAnqg7Qh7frhX+QhMGeJ9SpLpnG76qN85hLyjvYR6gRYiLT
# j8G4fxjqV400/buPw0rBo9yJZyq/8cGdCOLPOYf9qpefTcrZKeO9pg+UJHx8UABb
# aTqizRxm8sUSEycccCCFhIit3AI/3wGuSOADeiA676NcQ7+dsP04YvzvdN7pTZNy
# g672WpBDJdg0eItHl4yomvNeIOWh1qs4QqOW6TI8Ocsi4uSDTLorxLUDnBTwKQzz
# pxd1YUctEgRh32R0J9FkFkeEruJU94RljFr1uDCCBrkwggShoAMCAQICEQCZo4AK
# JlU7ZavcboSms+o5MA0GCSqGSIb3DQEBDAUAMIGAMQswCQYDVQQGEwJQTDEiMCAG
# A1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVt
# IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3Rl
# ZCBOZXR3b3JrIENBIDIwHhcNMjEwNTE5MDUzMjE4WhcNMzYwNTE4MDUzMjE4WjBW
# MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu
# MSQwIgYDVQQDExtDZXJ0dW0gQ29kZSBTaWduaW5nIDIwMjEgQ0EwggIiMA0GCSqG
# SIb3DQEBAQUAA4ICDwAwggIKAoICAQCdI88EMCM7wUYs5zNzPmNdenW6vlxNur3r
# Lfi+5OZ+U3iZIB+AspO+CC/bj+taJUbMbFP1gQBJUzDUCPx7BNLgid1TyztVLn52
# NKgxxu8gpyTr6EjWyGzKU/gnIu+bHAse1LCitX3CaOE13rbuHbtrxF2tPU8f253Q
# gX6eO8yTbGps1Mg+yda3DcTsOYOhSYNCJiL+5wnjZ9weoGRtvFgMHtJg6i671OPX
# IciiHO4Lwo2p9xh/tnj+JmCQEn5QU0NxzrOiRna4kjFaA9ZcwSaG7WAxeC/xoZSx
# F1oK1UPZtKVt+yrsGKqWONoK6f5EmBOAVEK2y4ATDSkb34UD7JA32f+Rm0wsr5aj
# zftDhA5mBipVZDjHpwzv8bTKzCDUSUuUmPo1govD0RwFcTtMXcfJtm1i+P2UNXad
# PyYVKRxKQATHN3imsfBiNRdN5kiVVeqP55piqgxOkyt+HkwIA4gbmSc3hD8ke66t
# 9MjlcNg73rZZlrLHsAIV/nJ0mmgSjBI/TthoGJDydekOQ2tQD2Dup/+sKQptalDl
# ui59SerVSJg8gAeV7N/ia4mrGoiez+SqV3olVfxyLFt3o/OQOnBmjhKUANoKLYlK
# mUpKEFI0PfoT8Q1W/y6s9LTI6ekbi0igEbFUIBE8KDUGfIwnisEkBw5KcBZ3XwnH
# mfznwlKo8QIDAQABo4IBVTCCAVEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
# 3XRdTADbe5+gdMqxbvc8wDLAcM0wHwYDVR0jBBgwFoAUtqFUOQLDoD+Oirz61Pgc
# ptE6Dv0wDgYDVR0PAQH/BAQDAgEGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMDAGA1Ud
# HwQpMCcwJaAjoCGGH2h0dHA6Ly9jcmwuY2VydHVtLnBsL2N0bmNhMi5jcmwwbAYI
# KwYBBQUHAQEEYDBeMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1jZXJ0
# dW0uY29tMDIGCCsGAQUFBzAChiZodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0ucGwv
# Y3RuY2EyLmNlcjA5BgNVHSAEMjAwMC4GBFUdIAAwJjAkBggrBgEFBQcCARYYaHR0
# cDovL3d3dy5jZXJ0dW0ucGwvQ1BTMA0GCSqGSIb3DQEBDAUAA4ICAQB1iFgP5Y9Q
# KJpTnxDsQ/z0O23JmoZifZdEOEmQvo/79PQg9nLF/GJe6ZiUBEyDBHMtFRK0mXj3
# Qv3gL0sYXe+PPMfwmreJHvgFGWQ7XwnfMh2YIpBrkvJnjwh8gIlNlUl4KENTK5DL
# qsYPEtRQCw7R6p4s2EtWyDDr/M58iY2UBEqfUU/ujR9NuPyKk0bEcEi62JGxauFY
# zZ/yld13fHaZskIoq2XazjaD0pQkcQiIueL0HKiohS6XgZuUtCKA7S6CHttZEsOb
# QJ1j2s0urIDdqF7xaXFVaTHKtAuMfwi0jXtF3JJphrJfc+FFILgCbX/uYBPBlbBI
# P4Ht4xxk2GmfzMn7oxPITpigQFJFWuzTMUUgdRHTxaTSKRJ/6Uh7ki/pFjf9sUAS
# WgxT69QF9Ki4JF5nBIujxZ2sOU9e1HSCJwOfK07t5nnzbs1LbHuAIGJsRJiQ6HX/
# DW1XFOlXY1rc9HufFhWU+7Uk+hFkJsfzqBz3pRO+5aI6u5abI4Qws4YaeJH7H7M8
# X/YNoaArZbV4Ql+jarKsE0+8XvC4DJB+IVcvC9Ydqahi09mjQse4fxfef0L7E3hh
# o2O3bLDM6v60rIRUCi2fJT2/IRU5ohgyTch4GuYWefSBsp5NPJh4QRTP9DC3gc5Q
# EKtbrTY0Ka87Web7/zScvLmvQBm8JDFpDjCCBrkwggShoAMCAQICEQDn/2nHOzXO
# S5Em2HR8aKWHMA0GCSqGSIb3DQEBDAUAMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UE
# ChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl
# cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBO
# ZXR3b3JrIENBIDIwHhcNMjEwNTE5MDUzMjA3WhcNMzYwNTE4MDUzMjA3WjBWMQsw
# CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMSQw
# IgYDVQQDExtDZXJ0dW0gVGltZXN0YW1waW5nIDIwMjEgQ0EwggIiMA0GCSqGSIb3
# DQEBAQUAA4ICDwAwggIKAoICAQDpEh8ENe25XXrFppVBvoplf0530W0lddNmjtv4
# YSh/f7eDQKFaIqc7tHj7ox+u8vIsJZlroakUeMS3i3T8aJRC+eQs4FF0GqvkM6+W
# ZO8kmzZfxmZaBYmMLs8FktgFYCzywmXeQ1fEExflee2OpbHVk665eXRHjH7MYZIz
# Nnjl2m8Hy8ulB9mR8wL/W0v0pjKNT6G0sfrx1kk+3OGosFUb7yWNnVkWKU4qSxLv
# 16kJ6oVJ4BSbZ4xMak6JLeB8szrK9vwGDpvGDnKCUMYL3NuviwH1x4gZG0JAXU3x
# 2pOAz91JWKJSAmRy/l0s0l5bEYKolg+DMqVhlOANd8Yh5mkQWaMEvBRE/kAGzIqg
# WhwzN2OsKIVtO8mf5sPWSrvyplSABAYa13rMYnzwfg08nljZHghquCJYCa/xHK9a
# cev9UD7Y+usr15d7mrszzxhF1JOr1Mpup2chNSBlyOObhlSO16rwrffVrg/SzaKf
# SndS5swRhr8bnDqNJY9TNyEYvBYpgF95K7p0g4LguR4A++Z1nFIHWVY5v0fNVZmg
# zxD9uVo/gta3onGOQj3JCxgYx0KrCXu4yc9QiVwTFLWbNdHFSjBCt5/8Q9pLuRhV
# ocdCunhcHudMS1CGQ/Rn0+7P+fzMgWdRKfEOh/hjLrnQ8BdJiYrZNxvIOhM2aa3z
# EDHNwwIDAQABo4IBVTCCAVEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUvlQC
# L79AbHNDzqwJJU6eQ0Qa7uAwHwYDVR0jBBgwFoAUtqFUOQLDoD+Oirz61PgcptE6
# Dv0wDgYDVR0PAQH/BAQDAgEGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMDAGA1UdHwQp
# MCcwJaAjoCGGH2h0dHA6Ly9jcmwuY2VydHVtLnBsL2N0bmNhMi5jcmwwbAYIKwYB
# BQUHAQEEYDBeMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1jZXJ0dW0u
# Y29tMDIGCCsGAQUFBzAChiZodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0ucGwvY3Ru
# Y2EyLmNlcjA5BgNVHSAEMjAwMC4GBFUdIAAwJjAkBggrBgEFBQcCARYYaHR0cDov
# L3d3dy5jZXJ0dW0ucGwvQ1BTMA0GCSqGSIb3DQEBDAUAA4ICAQC4k1l3yUwV/ZQH
# CKCneqAs8EGTnwEUJLdDpokN/dMhKjK0rR5qX8nIIHzxpQR3TAw2IRw1Uxsr2Pli
# G3bCFqSdQTUbfaTq6V3vBzEebDru9QFjqlKnxCF2h1jhLNFFplbPJiW+JSnJTh1f
# KEqEdKdxgl9rVTvlxfEJ7exOn25MGbd/wGPwuSmMxRJVO0wnqgS7kmoJjNF9zqee
# hFSDDP8ZVkWg4EZ2tIS0M3uZmByRr+1Lkwjjt8AtW83mVnZTyTsOb+FNfwJY7DS4
# FmWhkRbgcHRetreoTirPOr/ozyDKhT8MTSTf6Lttg6s6T/u08mDWw6HK04ZRDfQ9
# sb77QV8mKgO44WGP31vXnVKoWVJpFBjPvjL8/Zck/5wXX2iqjOaLStFOR/IQki+E
# hn4zlcgVm22ZVCBPF+l8nAwUUShCtKuSU7GmZLKCmmxQMkSiWILTm8EtVD6AxnJh
# oq8EnhjEEyUoflkeRF2WhFiVQOmWTwZRr44IxWGkNJC6tTorW5rl2Zl+2e9JLPYf
# 3pStAPMDoPKIjVXd6NW2+fZrNUBeDo2eOa5Fn7Brs/HLQff5Xgris5MeUbdVgDrF
# 8uxO6cLPvZPo63j62SsNg55pTWk9fUIF9iPoRbb4QurjoY/woI1RAOKtYtTic6aA
# Jq3u83RIPpGXBSJKwx4KJAOZnCDCtTGCBqIwggaeAgEBMGowVjELMAkGA1UEBhMC
# UEwxITAfBgNVBAoTGEFzc2VjbyBEYXRhIFN5c3RlbXMgUy5BLjEkMCIGA1UEAxMb
# Q2VydHVtIENvZGUgU2lnbmluZyAyMDIxIENBAhBX5gfP8LabvnxCXoItEBa/MA0G
# CWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZI
# hvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcC
# ARUwLwYJKoZIhvcNAQkEMSIEIH2TuEljQ4qsQrUmSPsnmTuL8wi88U3d1UcjYSrK
# 2dtrMA0GCSqGSIb3DQEBAQUABIIBgJ/0gGbTDnsma+GIzoIhQmWvEAD98yK0OUYk
# 3/sfC0a4n/VHc2P2/oADHGax/9Hl7OLn2pb9KCWkY0i3uc9/WVCaxdGUDA98R1jN
# sg+Jx9c7dfoEY+vgQ5znCiQrYebZhN8jWqtIH93aEIDsPhPIWKwyJ7IV29V1Zq3l
# UIqgtuGV2dYkdWKDvuZri9tmpDk0UIMvAOw1wZQXtpKnX41LaEH0Apluj3j5lhhS
# TNxZoBi1UbF4/VF/eW83IBBIHZK4sMVx3irCoHKp4ISUBnagwgn6vKFbMBZ9dTC9
# ou7Vj6gKyCHe0OSX9ora6R8CLDFYyc98AuhrQ+VlIlho/LLXEtIPibKX/vNWuzOo
# aD1jBP5/oYzAp+/Hm+WA8bfTzjEUhzqVd4zXKMSCSHxjX4rrDu1ty3wIGNhKuq8T
# h7mTVLc/Hx24Q9elWVLP5fOGp8TnmEerNaaQzy59ksn4E1KwR6joVHSDwvAs32ku
# iDnszOlUIauvJauWbAHYpAfb+dAjPKGCBAIwggP+BgkqhkiG9w0BCQYxggPvMIID
# 6wIBATBqMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0
# ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBUaW1lc3RhbXBpbmcgMjAyMSBDQQIQ
# KPB3wRw2vf5fdDJHcCcuAzANBglghkgBZQMEAgIFAKCCAVYwGgYJKoZIhvcNAQkD
# MQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNjEwMTMwOTQwMzdaMDcG
# CyqGSIb3DQEJEAIvMSgwJjAkMCIEIIW+kOEK0kONfMkotq9IsJqyCBd87PiwEmxY
# 05EFJcQ8MD8GCSqGSIb3DQEJBDEyBDCeJNp/Len369CZ7cgVizcJQbpeTOghfwrA
# V5b9c5E28rzv+mjvPaFxYbzIcdO3S5MwgZ8GCyqGSIb3DQEJEAIMMYGPMIGMMIGJ
# MIGGBBRXFGhBDKha80JO+RZKUTYQ9NONmDBuMFqkWDBWMQswCQYDVQQGEwJQTDEh
# MB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMSQwIgYDVQQDExtDZXJ0
# dW0gVGltZXN0YW1waW5nIDIwMjEgQ0ECECjwd8EcNr3+X3QyR3AnLgMwDQYJKoZI
# hvcNAQEBBQAEggIAVJDaMlQJ6JtQZr+YJb5ckgr4UXrSGFjlBQRMncsNWIjF5OmC
# EKWgk9+6OdlMWTWfGQOqn37AHuSFXpMa71RDbUPjonDQblGPSlqUY5qid2/m/AKh
# ZVnk5aoh1HBVY553aGwMuAwWqsjSR4ISgTNkx1Fsa1oe1uhpdi3nTmlNMdcoNGll
# rH2YH+bEdNPCD1OdClrU/7G7vb7hZ2a+HATIMawq6az6sReZlan9PEimS8D9NAcb
# EKX+uLhjtOxslsDAfgztlE8+VM3pe0vGNur9ptPifC7kPJlK+mev3JNgat+U7L4R
# B2aN0J3v4kcU/+2n39Dt00mam4zfmcBg2I+I3JnQ1yWl7mtS6h8JCzWJ2xB51b9m
# uY5FOXuOrgLkIFNxlVKvCgfSeWNmYuc1n4OYqCpn8jMU0qZIamDzAGQFw2nRHpgj
# SCZeA/5FVtlRvh0TYBGCG2Q7FI+7FB/r0aCyXoJCjM5aArj3X3l5Ax5CEULWEUzX
# XnehVjTauVa4KXWuwyzxcpZ/7vFzfsw0LrLIShlx0vvK0GeSUHaB0IWFHfA/A6h7
# K9gN+HsbQL8DytWEl5nIuIcVVJT3T1SH4nQAns4Qh2hyyJwCO5yfME909Jptm4Nv
# IfGX8pr4iwZAGY07lvnojo8AkP0AGQvCJH4ARrbG1xBazC8sXaqRow28P58=
# SIG # End signature block