Public/Configuration/New-EntraOpsConfigFile.ps1
|
<# .SYNOPSIS Create config file to store required or optional parameters for executing EntraOps without end-user prompts. .DESCRIPTION Creates a configuration file used by other cmdlets, for example to create the required app registration and grant permissions. Connect-EntraOps can also import the file so the same settings are available to interactive and automated runs. .PARAMETER TenantName Name of the tenant (e.g. contoso.onmicrosoft.com) to create the configuration for. The tenant ID is resolved from the name, and the initial domain of the tenant is written as TenantName to the config file. .PARAMETER ManagingTenantName Name of the governing (managing) tenant of a Tenant Governance relationship, used to classify privileged access across tenants. Written as ManagingTenantName. Default is empty. .PARAMETER ManagingTenantId Tenant ID of the governing (managing) tenant of a Tenant Governance relationship. Written as ManagingTenantId. Default is empty. .PARAMETER AuthenticationType Defines the authentication method used to request tokens for Microsoft Graph, Azure Resource Manager, and other APIs. Default is FederatedCredentials. .PARAMETER UseInvokeRestMethodOnly Defines if all Invoke-EntraOps*Query cmdlets should use Invoke-RestMethod instead of the Microsoft Graph SDK (Invoke-MgGraphRequest). Default is false. In this mode the Microsoft Graph SDK is neither required nor installed; tokens are provided via Connect-EntraOps -MsGraphAccessToken or acquired with Get-AzAccessToken from the Az PowerShell context. Written as top-level setting "UseInvokeRestMethodOnly" to the config file and read by Connect-EntraOps; an explicit Connect-EntraOps -UseInvokeRestMethodOnly parameter wins over the config file. Note: REST-only parallel object resolution pre-warms a Graph token from the Az PowerShell context. If that token cannot be prepared, resolution falls back to sequential processing. .PARAMETER IncludeObjectDetails Include display names, UPNs, and detailed object-specific errors in console output. Object IDs are always shown. Written as ConsoleOutput.IncludeObjectDetails and disabled by default for automation-log privacy. .PARAMETER DevOpsPlatform Defines the platform where the EntraOps repository is hosted. Supported values are GitHub, AzureDevOps and None. Default is GitHub. .PARAMETER AzureDevOpsServiceConnection Name of the Azure Resource Manager service connection used by the Azure DevOps pipelines. Written as AzureDevOpsServiceConnection and applied to the pipeline definitions by Update-EntraOpsAzureDevOpsSchedules. Default is EntraOps-ServiceConnection. Not used with other DevOps platforms. .PARAMETER ConfigFilePath Location of the config file which will be created. Default is ./EntraOpsConfig.json. .PARAMETER IngestToLogAnalytics Defines if the classification data should be ingested to Log Analytics or Microsoft Sentinel Workspace. Default is false. .PARAMETER IngestToWatchLists Defines if the classification data should be ingested to Microsoft Sentinel WatchLists. Default is false. .PARAMETER ApplyAutomatedControlPlaneScopeUpdate Defines whether role-assignment scopes containing Control Plane assets should be updated before privileges are analyzed. Default is false. You are able to customize in the config file the data source ("EntraOps", "PrivilegedRolesFromAzGraph", "PrivilegedEdgesFromExposureManagement") and which criticality level or role definition/scope level should be considered. .PARAMETER ClassificationParameterScope Array of RBAC systems whose classification parameter files are (re)generated by Update-EntraOpsClassificationControlPlaneScope, written to the config file as AutomatedControlPlaneScopeUpdate.ClassificationParameterScope. Default is all supported RBAC systems ("Azure", "EntraID", "IdentityGovernance", "ResourceApps", "DeviceManagement", "Defender"). This is distinct from EntraOpsScopes (RbacSystems parameter/AutomatedControlPlaneScopeUpdate.EntraOpsScopes in the config file), which selects which RBAC systems' already-classified EAM export data is read as input to discover privileged objects. ClassificationParameterScope instead selects which RBAC systems' classification template/parameter files are written as output during that same run. .PARAMETER ApplyAutomatedClassificationUpdate Defines whether classification templates should be updated from "https://github.com/Cloud-Architekt/AzurePrivilegedIAM" before privileges are analyzed. Default is false. You are able to define which classification files should be updated in the config file later. .PARAMETER FailOnContradictoryTierPair Defines if generated artifact validation fails when an object's tier level contradicts its tier name (e.g. 0 with ManagementPlane). Written as GeneratedArtifactValidation.FailOnContradictoryTierPair. Default is false (reported as warning). .PARAMETER FailOnPrivilegedAssignmentWithoutClassification Defines if generated artifact validation fails when an assignment marked as privileged has no classification. Written as GeneratedArtifactValidation.FailOnPrivilegedAssignmentWithoutClassification. Default is false (reported as warning). .PARAMETER ApplyAutomatedEntraOpsUpdate Defines whether the Update-EntraOps workflow and its weekly schedule are enabled. Default is false. Workflow definitions are excluded from the default automated target set because GITHUB_TOKEN cannot publish changes below .github/workflows. Update them interactively with Update-EntraOps, or explicitly add the workflow target after configuring the documented EntraOps update publisher GitHub App. When enabled with the default PullRequest publication mode, the repository setting "Allow GitHub Actions to create and approve pull requests" (Settings > Actions > General > Workflow permissions) must be turned on, otherwise pull request creation fails. .PARAMETER UpdateRepository Repository name below the Cloud-Architekt organization that serves EntraOps updates. Default is "EntraOps" (public release channel, no credentials). "EntraOps-Insiders" is the private preview channel and requires the EntraOpsUpdatePat repository secret. Both are declared in the local EntraOpsUpdateContract.json; another repository is accepted only when it publishes a matching contract. .PARAMETER ValidationFrequency Controls candidate validation in the Update-EntraOps workflow. OnChange (default) validates new source commits and validation-setting changes, Always validates every workflow run, and Never applies changed candidates without executing their validation suite. .PARAMETER RunBrowserTests Defines whether update-candidate validation includes the Playwright documentation and report tests. Default is true. This setting has no effect when ValidationFrequency is Never. .PARAMETER UpdatePublicationMode Controls how the Update-EntraOps workflow publishes an applied candidate. PullRequest (default) creates or updates a review branch and pull request; DirectPush commits to the current branch without review. Applies to the GitHub workflow and the Azure DevOps update pipeline. .PARAMETER ApplyConditionalAccessTargetGroups Defines if security groups per tier for targeting Conditional Access policies should be created and updated (AutomatedConditionalAccessTargetGroups) as part of the push pipeline. Default is false. .PARAMETER ApplyAdministrativeUnitAssignments Defines if Administrative Units per tier should be created and updated with privileged users and groups (AutomatedAdministrativeUnitManagement) as part of the push pipeline. Default is false. .PARAMETER ApplyRmauAssignmentsForUnprotectedObjects Defines if privileged users and groups without restricted management should be added to a Restricted Management Administrative Unit (AutomatedRmauAssignmentsForUnprotectedObjects) as part of the push pipeline. Default is false. .PARAMETER ApplyPrivilegedElmCatalogProtection Defines if Entitlement Management catalogs with Control Plane scope should be updated to privileged catalogs (Update-EntraOpsPrivilegedUnprotectedElmCatalog) as part of the push pipeline. Default is false. Caution: Enabling this setting requires the elevated Microsoft Graph permission "EntitlementManagement.ReadWrite.All" which will be assigned by New-EntraOpsWorkloadIdentity only if this setting is enabled. .PARAMETER IncludeUnprotectedDevices Defines if devices owned by or associated with privileged users should be added to the same tier's unprotected-objects Restricted Management Administrative Unit when they are not already protected by another RMAU. Default is false. .PARAMETER RemovalSafetyThreshold Safety brake for every automated cmdlet that removes protections from classified EAM data (Conditional Access target groups, Administrative Units, Restricted Management AUs including devices, and Entitlement Management catalog privilege levels). The value is the fraction (0..1) of a target's currently protected members/objects that a single run may remove. Each target plans all eligible removals before mutation; when the plan exceeds Ceiling(current count x threshold), no removals are applied to that target, a SafetyAbort is recorded, and the command fails after its summary. Non-destructive additions or protection operations may still proceed. Example with the default 0.5: a CA group with 20 current members permits at most 10 removals; a plan for 11 aborts before group removals begin. Setting the value to 1.0 permits removal of the complete current protected set and therefore disables threshold-based SafetyAbort protection for automated runs. After reviewing the reported plan, run the affected cmdlet manually with -ForceRemovalBeyondSafetyThreshold to apply an intentional oversized reconciliation. The force switch is deliberately not configurable because bypassing the brake must remain an explicit invocation-time decision. Written to all four mutation sections of the config file. Default is 0.5. .PARAMETER ClassifyConstrainedDelegationAlwaysAsControlPlane Defines if Azure RBAC role assignments with an authorization/role assignment ABAC condition (constrained delegation) should always be classified as Control Plane instead of being dynamically downgraded based on the scope and the role definitions allowed by the condition. Default is false (dynamic downgrade). .PARAMETER UnresolvedRoleDefinitionFallbackTier Tier assigned to Azure RBAC role assignments whose role definition could not be resolved from Azure Resource Graph (e.g. ARG replication lag for a new custom role, deleted role definition or a transient ARG failure). Allowed values: "Unclassified" (default, consistent with the Unclassified fallback of the other RBAC systems), "ManagementPlane" or "ControlPlane" (stricter fail-closed tiers) or "None" (skip classification). .PARAMETER DeletedPrincipalAssignmentHandling Controls Azure RBAC assignments whose principal is confirmed deleted by HTTP 404 responses from both Microsoft Graph identity endpoints. "Filter" (default) removes it from Azure EAM output; "Keep" preserves the unresolved assignment. Other resolution failures are never filtered. .PARAMETER ApplyAutomatedReportingGeneration Defines if the EntraOps Reporting static web apps (Classification Explorer, Tier Breach Analyzer, EAM Dashboard and Access Path Map) should be regenerated automatically by the Push-EntraOpsPrivilegedReporting workflow. Default is false. The generated report is uploaded as a downloadable workflow artifact; it is not committed back to the repository. The trigger for this workflow is controlled by the config file's `WorkflowTrigger` section: by default (`PushReportingAfterPullWorkflowTrigger` set to `true`) it runs right after every successful Pull-EntraOpsPrivilegedEAM run. It can additionally/alternatively be run on a recurring schedule by enabling `PushReportingScheduledTrigger` and customizing the cron expression in `PushReportingScheduledCron` (default is weekly, every Monday at 09:00 UTC). Disable both settings to only allow manual (`workflow_dispatch`) runs. .PARAMETER PublishReportsAsRelease Publishes generated reports as GitHub Releases in addition to the 30-day workflow artifact. Disabled by default because each release contains a full tenant-data snapshot. .PARAMETER RunGeneratedReportTests Installs npm dependencies and runs browser tests against generated tenant reports in reporting automation. Disabled by default because third-party dependencies can access tenant report data and the network. .PARAMETER UploadReportTestDiagnostics Uploads Playwright traces of failed report tests as a 1-day artifact (EntraOps-Reporting-TestDiagnostics). Disabled by default because the traces contain tenant report data. Used only with RunGeneratedReportTests. .PARAMETER ReportingReleasesToKeep Number of `reporting-*` GitHub Releases to retain when release publishing is enabled. Default is 10. Accepts 1 to 1000, so a generated configuration always prunes. Turning pruning off is deliberately not offered here because every retained release holds a full tenant-data snapshot; set `AutomatedReportingGeneration.ReportingReleasesToKeep` to an empty value in the Configuration Wizard or directly in the configuration file if unbounded retention is really intended. .PARAMETER EnablePrivilegeHistory Defines if the Privilege History report data is generated by New-EntraOpsReportingData. Written as PrivilegeHistory.EnablePrivilegeHistory. Default is true. .PARAMETER PrivilegeHistoryTimeRangeInDays Number of days the Privilege History samples back into the git history. Written as PrivilegeHistory.TimeRangeInDays. Default is empty (full history). .PARAMETER PrivilegeHistorySnapshotInterval ISO 8601 duration of the Privilege History snapshot interval. Written as PrivilegeHistory.SnapshotInterval. Default is "P2W" (every 2 weeks). .PARAMETER AccessPathMapResolveObjectIdsOutsidePrivilegedEAM Defines if New-EntraOpsAccessPathMapData should resolve edge endpoints that reference an object id outside the Privileged EAM export (e.g. a standard, non-privileged work account or a group/application owned by a privileged principal - referenced via OwnedObjects, Owners, AssociatedPawDevice or AssociatedWorkAccount) via Microsoft Graph to their display name and object type, instead of showing them as an anonymous "unresolved" placeholder node in Access Path Map. Default is true. Requires an active Microsoft Graph connection with at least directory read permissions (User.Read.All, Group.Read.All, Application.Read.All or Directory.Read.All) when generating the Access Path Map data. .PARAMETER EamDashboardResolveLinkedIdentityObjectIds Defines if New-EntraOpsPrivilegedEamDashboardData should resolve linked identity object IDs outside the Privileged EAM export to display names through Microsoft Graph. Default is true. Requires an active Microsoft Graph connection with directory read permissions when generating EAM Dashboard data. .PARAMETER PrivilegedAssetsResolveRelatedObjectIds Defines if New-EntraOpsPrivilegedAssetsData should resolve related object IDs outside the Privileged EAM export (owners, sponsors, owned objects, owned devices, identity parent, associated work account and PAW device) to display name and object type through Microsoft Graph. Default is true. Requires an active Microsoft Graph connection with directory read permissions when generating Privileged Assets data. .PARAMETER ClassificationExplorerGenerateChangeHistory Defines if New-EntraOpsClassificationExplorerData should derive the classification change history from the source repository's git log. Default is false because the derivation requires a full AzurePrivilegedIAM checkout and is rarely needed in deployment reporting. Written as ClassificationExplorer.GenerateChangeHistory; a missing configuration file or setting also skips history generation. .PARAMETER ResolveGroupMembersForPrivilegedAssets Defines if Configuration Analyzer should resolve included Conditional Access and authentication method policy target groups through PIM-aware transitive membership. Default is true. .PARAMETER AllowPartialTenantGovernanceSnapshot Defines if the Push-EntraOpsPrivilegedReporting workflow should generate Configuration Analyzer data from a partially successful Tenant Governance snapshot. Failed resource types remain marked as stale in the report. Default is true. .PARAMETER MaxTenantGovernanceSnapshotAgeHours Maximum age in hours of the published Tenant Governance snapshot before the Configuration Analyzer treats it as stale. Default is $null, which derives the limit from TenantGovernanceSnapshotScheduledCron: 30 hours for a daily, 174 hours for a weekly and 750 hours for a monthly schedule. .PARAMETER StaleTenantGovernanceSnapshotAction Defines what automated reporting does when the Tenant Governance snapshot is older than its maximum age. Warn (default) generates the Configuration Analyzer from it and marks the report as outdated, Skip leaves the Configuration Analyzer out and publishes the other reports, Fail stops the run before any report is generated. .PARAMETER ConfigurationAnalyzerTimeRangeInDays Only Tenant Governance snapshots of the last N days are included in the Configuration Analyzer dataset. Default is 365. Use $null for the full history. .PARAMETER ConfigurationAnalyzerSnapshotInterval Minimum spacing between two Configuration Analyzer snapshots as ISO 8601 duration (P1D, P1W, P1M, ...) or None to keep every snapshot commit. Default is P1W (weekly), which keeps the dataset size bounded. .PARAMETER PimRequestFlowExcludedRiskFlags Array of PIM Request Flow risk-flag IDs to exclude from the generated report. Default is empty, so every supported risk flag is reported. Use the Configuration Wizard to select flags by name. .PARAMETER AccessPackageFlowExcludedRiskFlags Array of Access Package Flow risk-flag IDs to exclude from the generated report. Default is empty, so every supported risk flag is reported. .PARAMETER ConditionalAccessAnalysisExcludedFindings Array of Conditional Access Analysis finding IDs to exclude from the generated report. Default is empty, so every supported finding is reported. .PARAMETER EidscaExcludedFindings Array of EIDSCA check IDs to exclude from the generated report. Default is empty. .PARAMETER EnableCustomSecurityAttributeClassification Defines if the tier of User, ServicePrincipal and Application objects is read from Custom Security Attributes (CustomSecurityAttributes.Enabled). Can be combined with Alternate Tier Level Attributes and the Object Classification File: Custom Security Attributes win, an object without tier falls back to the filters of its object type, then to the file. Default is true, or false when -EnableObjectClassificationFile is $true and this parameter isn't passed. The PAW device and work account attributes are always read. .PARAMETER EnableAlternateObjectTierLevelAttributes Defines if User and ServicePrincipal objects should be classified by PowerShell filter expressions evaluated against their own resolved EntraOps details (section AlternateObjectTierLevelAttributes) when they have no Custom Security Attribute tier. Sets User.Enabled and ServicePrincipal.Enabled; Group.Enabled is always created as false. Default is false. The config file always includes the section with empty filter expressions so it can be filled in and enabled per object type later; see Docs "Classify by Alternate Tier Level Attributes" for the required syntax. .PARAMETER EnableObjectClassificationFile Defines if the Object Classification File (section ObjectClassificationFile) is used to classify User, Group, ServicePrincipal and Application objects by ObjectId. A file entry applies to objects that neither Custom Security Attributes nor Alternate Tier Level Attributes classify. Default is false. The file can be maintained in the Object Classification view of the Privileged Assets reporting app. .PARAMETER ObjectClassificationFilePath Path of the Object Classification File (.json or .csv), relative to the EntraOps repository root. Default is "./Classification/ObjectClassification.json". .PARAMETER EnableTenantGovernanceSnapshot Defines if the EntraOps Tenant Governance Snapshot feature is enabled. Default is false. When enabled, run New-EntraOpsWorkloadIdentity afterwards to grant the workload identity the required "ConfigurationMonitoring.ReadWrite.All" Microsoft Graph permission, and to configure the read permissions of the first-party "Microsoft Tenant Configuration Management" (UTCM) service principal for the resources configured in `TenantGovernanceSnapshot.ResourcesToInclude`. Snapshots are captured by Save-EntraOpsTenantGovernanceSnapshotJson (and the Pull-EntraOpsTenantGovernance workflow) and stored as per-resource files under `TenantGovernance/Snapshots`; historical versions are tracked via Git history. Reference: https://learn.microsoft.com/en-us/graph/utcm-entra-resources .PARAMETER TenantGovernanceResourcesToInclude Array of Microsoft Entra resource types (e.g. "microsoft.entra.conditionalAccessPolicy") to capture in each Tenant Governance snapshot. Defaults to the recommended tenant governance resource set. .PARAMETER TenantGovernanceSnapshotScheduledCron Cron expression for the "start" scheduled trigger of the Pull-EntraOpsTenantGovernance workflow, which creates the snapshot job without waiting for it to complete. Default is daily at 06:00 UTC ("0 6 * * *"). .PARAMETER TenantGovernanceSnapshotScheduledCronComplete Cron expression for the first "collect" trigger of the Pull-EntraOpsTenantGovernance workflow, which checks the pending snapshot job once without polling. Default is daily at 07:00 UTC. .PARAMETER TenantGovernanceSnapshotScheduledCronCompleteRetry1 Cron expression for the first collection retry. Default is daily at 07:30 UTC. .PARAMETER TenantGovernanceSnapshotScheduledCronCompleteRetry2 Cron expression for the final collection retry. Default is daily at 08:00 UTC. .PARAMETER TenantGovernanceSnapshotResourceFileNaming Controls the file name used for each persisted Tenant Governance snapshot resource file. "ResourceId" (default) names files after the immutable resource Id; "DisplayName" produces readable paths but changes them on rename. Resource-ID naming falls back to a display-name path for singleton types without an Id. .PARAMETER RbacSystems Array of RBAC systems to collect and classify. Written as RbacSystems and as AutomatedControlPlaneScopeUpdate.EntraOpsScopes. Default is Azure, EntraID, IdentityGovernance, ResourceApps and DeviceManagement; Defender is optional. .PARAMETER WatchListTemplates Enrichment WatchLists created by Save-EntraOpsPrivilegedEAMEnrichmentToWatchLists ("None", "All", "VIPUsers", "HighValueAssets", "IdentityCorrelation"). Written as SentinelWatchLists.WatchListTemplates. Default is "None". .PARAMETER WatchListWorkloadIdentity Workload identity enrichment WatchLists created by Save-EntraOpsWorkloadIdentityEnrichmentWatchLists ("All", "ManagedIdentityAssignedResourceId", "WorkloadIdentityAttackPaths", "WorkloadIdentityInfo", "WorkloadIdentityRecommendations"). Written as SentinelWatchLists.WatchListWorkloadIdentity. Default is "None". .EXAMPLE Create a configuration file in the default location, update classification files before analyzing privileges, and enable both Log Analytics and Sentinel WatchList ingestion. New-EntraOpsConfigFile -TenantName "contoso.onmicrosoft.com" -ApplyAutomatedControlPlaneScopeUpdate $true -IngestToLogAnalytics $true -IngestToWatchLists $true -ApplyAutomatedClassificationUpdate $true .EXAMPLE Create environment file for Azure DevOps deployment with core monitoring only. New-EntraOpsConfigFile -TenantName "contoso.onmicrosoft.com" -DevOpsPlatform "AzureDevOps" -ApplyAutomatedClassificationUpdate $true #> function New-EntraOpsConfigFile { [CmdletBinding()] param ( [parameter(Mandatory = $true)] [string]$TenantName, [parameter(Mandatory = $false)] [string]$ManagingTenantName = "", [parameter(Mandatory = $false)] [string]$ManagingTenantId = "", [Parameter(Mandatory = $false)] [ValidateSet('UserInteractive', 'SystemAssignedMSI', 'UserAssignedMSI', 'FederatedCredentials', 'AlreadyAuthenticated', 'DeviceAuthentication')] [string]$AuthenticationType = "FederatedCredentials", [Parameter(Mandatory = $false)] [boolean]$UseInvokeRestMethodOnly = $false, [Parameter(Mandatory = $false)] [boolean]$IncludeObjectDetails = $false, [Parameter(Mandatory = $false)] [ValidateSet('AzureDevOps', 'GitHub', 'None')] [string]$DevOpsPlatform = "GitHub", [Parameter(Mandatory = $false)] [ValidatePattern('^[^\r\n]+$')] [string]$AzureDevOpsServiceConnection = 'EntraOps-ServiceConnection', [Parameter(Mandatory = $false)] [ValidateScript({ Test-Path (Split-Path $_ -Parent) })] [string]$ConfigFilePath = "$EntraOpsBasefolder/EntraOpsConfig.json", [Parameter(Mandatory = $false)] [boolean]$IngestToLogAnalytics = $false, [Parameter(Mandatory = $false)] [boolean]$IngestToWatchLists = $false, [Parameter(Mandatory = $false)] [boolean]$ApplyAutomatedControlPlaneScopeUpdate = $false, [Parameter(Mandatory = $false)] [boolean]$ApplyAutomatedClassificationUpdate = $false, [Parameter(Mandatory = $false)] [boolean]$FailOnContradictoryTierPair = $false, [Parameter(Mandatory = $false)] [boolean]$FailOnPrivilegedAssignmentWithoutClassification = $false, [Parameter(Mandatory = $false)] [boolean]$ApplyAutomatedEntraOpsUpdate = $false, [Parameter(Mandatory = $false)] [ValidatePattern('^[A-Za-z0-9_.-]+$')] [string]$UpdateRepository = 'EntraOps', [Parameter(Mandatory = $false)] [ValidateSet('OnChange', 'Always', 'Never')] [string]$ValidationFrequency = 'OnChange', [Parameter(Mandatory = $false)] [boolean]$RunBrowserTests = $true, [Parameter(Mandatory = $false)] [ValidateSet('PullRequest', 'DirectPush')] [string]$UpdatePublicationMode = 'PullRequest', [Parameter(Mandatory = $false)] [boolean]$ApplyConditionalAccessTargetGroups = $false, [Parameter(Mandatory = $false)] [boolean]$ApplyAdministrativeUnitAssignments = $false, [Parameter(Mandatory = $false)] [boolean]$ApplyRmauAssignmentsForUnprotectedObjects = $false, [Parameter(Mandatory = $false)] [boolean]$IncludeUnprotectedDevices = $false, [Parameter(Mandatory = $false)] [ValidateRange(0, 1)] [double]$RemovalSafetyThreshold = 0.5, [Parameter(Mandatory = $false)] [boolean]$ApplyPrivilegedElmCatalogProtection = $false, [Parameter(Mandatory = $false)] [boolean]$ClassifyConstrainedDelegationAlwaysAsControlPlane = $false, [Parameter(Mandatory = $false)] [ValidateSet("Unclassified", "ManagementPlane", "ControlPlane", "None")] [string]$UnresolvedRoleDefinitionFallbackTier = "Unclassified", [Parameter(Mandatory = $false)] [ValidateSet("Keep", "Filter")] [string]$DeletedPrincipalAssignmentHandling = "Filter", [Parameter(Mandatory = $false)] [boolean]$ApplyAutomatedReportingGeneration = $false, [Parameter(Mandatory = $false)] [boolean]$PublishReportsAsRelease = $false, [Parameter(Mandatory = $false)] [boolean]$RunGeneratedReportTests = $false, [Parameter(Mandatory = $false)] [boolean]$UploadReportTestDiagnostics = $false, [Parameter(Mandatory = $false)] [ValidateRange(1, 1000)] [int]$ReportingReleasesToKeep = 10, [Parameter(Mandatory = $false)] [boolean]$EnablePrivilegeHistory = $true, [Parameter(Mandatory = $false)] [Nullable[int]]$PrivilegeHistoryTimeRangeInDays = $null, [Parameter(Mandatory = $false)] [string]$PrivilegeHistorySnapshotInterval = "P2W", [Parameter(Mandatory = $false)] [boolean]$AccessPathMapResolveObjectIdsOutsidePrivilegedEAM = $true, [Parameter(Mandatory = $false)] [boolean]$EamDashboardResolveLinkedIdentityObjectIds = $true, [Parameter(Mandatory = $false)] [boolean]$PrivilegedAssetsResolveRelatedObjectIds = $true, [Parameter(Mandatory = $false)] [boolean]$ClassificationExplorerGenerateChangeHistory = $false, [Parameter(Mandatory = $false)] [boolean]$ResolveGroupMembersForPrivilegedAssets = $true, [Parameter(Mandatory = $false)] [boolean]$AllowPartialTenantGovernanceSnapshot = $true, [Parameter(Mandatory = $false)] [System.Nullable[int]]$MaxTenantGovernanceSnapshotAgeHours, [Parameter(Mandatory = $false)] [ValidateSet('Warn', 'Skip', 'Fail')] [string]$StaleTenantGovernanceSnapshotAction = 'Warn', [Parameter(Mandatory = $false)] [System.Nullable[int]]$ConfigurationAnalyzerTimeRangeInDays = 365, [Parameter(Mandatory = $false)] [ValidatePattern('^(None|P\d+[DWMY])$')] [string]$ConfigurationAnalyzerSnapshotInterval = 'P1W', [Parameter(Mandatory = $false)] [ValidateSet('noMfaActivation', 'noApprovalActivation', 'approverNotEnforced', 'approverLowerTier', 'authContextMissing', 'permanentEligible', 'permanentActive', 'noMfaActiveAssign')] [string[]]$PimRequestFlowExcludedRiskFlags = @(), [Parameter(Mandatory = $false)] [ValidateSet('noApproval', 'broadRequestor', 'approverLowerTier', 'assignedMorePrivileged')] [string[]]$AccessPackageFlowExcludedRiskFlags = @(), [Parameter(Mandatory = $false)] [ValidateSet('policyNotEnforced', 'legacyAuthentication', 'allUserMfa', 'privilegedRoleControls', 'riskBasedPolicies', 'deviceControls', 'guestCoverage', 'clientAppCoverage', 'exclusionSurface')] [string[]]$ConditionalAccessAnalysisExcludedFindings = @(), [Parameter(Mandatory = $false)] [string[]]$EidscaExcludedFindings = @(), [Parameter(Mandatory = $false)] [boolean]$EnableCustomSecurityAttributeClassification = $true, [Parameter(Mandatory = $false)] [boolean]$EnableAlternateObjectTierLevelAttributes = $false, [Parameter(Mandatory = $false)] [boolean]$EnableObjectClassificationFile = $false, [Parameter(Mandatory = $false)] [ValidatePattern('\.(json|csv)$')] [string]$ObjectClassificationFilePath = "./Classification/ObjectClassification.json", [Parameter(Mandatory = $false)] [boolean]$EnableTenantGovernanceSnapshot = $false, [Parameter(Mandatory = $false)] [Array]$TenantGovernanceResourcesToInclude, [Parameter(Mandatory = $false)] [string]$TenantGovernanceSnapshotScheduledCron = "0 6 * * *", [Parameter(Mandatory = $false)] [string]$TenantGovernanceSnapshotScheduledCronComplete = "0 7 * * *", [Parameter(Mandatory = $false)] [string]$TenantGovernanceSnapshotScheduledCronCompleteRetry1 = "30 7 * * *", [Parameter(Mandatory = $false)] [string]$TenantGovernanceSnapshotScheduledCronCompleteRetry2 = "0 8 * * *", [Parameter(Mandatory = $false)] [ValidateSet("DisplayName", "ResourceId")] [string]$TenantGovernanceSnapshotResourceFileNaming = "ResourceId", [Parameter(Mandatory = $false)] [ValidateSet("Azure", "EntraID", "IdentityGovernance", "DeviceManagement", "ResourceApps", "Defender")] [Array]$RbacSystems = ("Azure", "EntraID", "IdentityGovernance", "ResourceApps", "DeviceManagement"), [Parameter(Mandatory = $false)] [ValidateSet("Azure", "EntraID", "IdentityGovernance", "DeviceManagement", "ResourceApps", "Defender")] [Array]$ClassificationParameterScope = ("Azure", "EntraID", "IdentityGovernance", "ResourceApps", "DeviceManagement", "Defender"), [Parameter(Mandatory = $false)] [ValidateSet("None", "All", "VIPUsers", "HighValueAssets", "IdentityCorrelation")] [Array]$WatchListTemplates = "None", [Parameter(Mandatory = $false)] [ValidateSet("ManagedIdentityAssignedResourceId", "All", "WorkloadIdentityAttackPaths", "WorkloadIdentityInfo", "WorkloadIdentityRecommendations")] [Array]$WatchListWorkloadIdentity = "None" ) $ErrorActionPreference = "Stop" # Validated here instead of [ValidateRange] because that attribute rejects an explicit $null (derive from schedule). if ($null -ne $MaxTenantGovernanceSnapshotAgeHours -and ($MaxTenantGovernanceSnapshotAgeHours -lt 1 -or $MaxTenantGovernanceSnapshotAgeHours -gt 8760)) { throw "MaxTenantGovernanceSnapshotAgeHours must be between 1 and 8760 (or null to derive it from TenantGovernanceSnapshotScheduledCron), but is $MaxTenantGovernanceSnapshotAgeHours." } #region Connect to Azure Resource Manager # Get TenantId try { $TenantId = (Invoke-RestMethod -Uri ("https://login.windows.net/$($TenantName)/.well-known/openid-configuration")).token_endpoint.split('/')[3] } catch { Write-Error "Can't find tenant with name $TenantName. Error: $_" } Write-Output "Connect to Azure Resource Manager..." $AzContext = Get-AzContext if ($AzContext.Tenant.Id -ne $TenantId) { Write-Verbose "Call Connect-AzAccount to $($TenantId)..." Connect-AzAccount -TenantId $TenantId } else { Write-Verbose "Already connected to $($AzContext.Tenant.Id)" } try { $TenantDetails = Get-AzTenant -TenantId $TenantId } catch { Write-Error "Failed to get Tenant details for TenantId $TenantId. Error: $_" } #endregion #region Create configuration file schema with default values $DefaultUpdateTargets = if ($DevOpsPlatform -eq 'AzureDevOps') { @("./.azure-pipelines", "./.github/scripts", "./Docs", "./EntraOps", "./Parsers", "./Queries", "./Reports", "./Samples", "./Tests", "./Workbooks", "./package.json", "./package-lock.json", "./playwright.config.mjs", "./CHANGELOG.md", "./EntraOpsUpdateContract.json") } else { @("./.github/actions", "./.github/agents", "./.github/scripts", "./Docs", "./EntraOps", "./Parsers", "./Queries", "./Reports", "./Samples", "./Tests", "./Workbooks", "./package.json", "./package-lock.json", "./playwright.config.mjs", "./CHANGELOG.md", "./EntraOpsUpdateContract.json") } if (-not $PSBoundParameters.ContainsKey('EnableCustomSecurityAttributeClassification')) { $EnableCustomSecurityAttributeClassification = -not $EnableObjectClassificationFile } # GitHub Actions and the Azure DevOps update pipeline both support PullRequest and DirectPush $DefaultUpdatePublicationMode = $UpdatePublicationMode $EnvConfigSchema = [ordered]@{ TenantId = $($TenantId) TenantName = $($TenantDetails.Domains[0]) ManagingTenantId = $($ManagingTenantId) ManagingTenantName = $($ManagingTenantName) AuthenticationType = $($AuthenticationType) UseInvokeRestMethodOnly = $UseInvokeRestMethodOnly ConsoleOutput = [ordered]@{ IncludeObjectDetails = $IncludeObjectDetails } ClientId = "Use New-EntraOpsWorkloadIdentity to create a new App Registration or enter here manually" DevOpsPlatform = $($DevOpsPlatform) AzureDevOpsServiceConnection = $AzureDevOpsServiceConnection RbacSystems = $($RbacSystems) AzureRbacClassification = [ordered]@{ ClassifyConstrainedDelegationAlwaysAsControlPlane = $ClassifyConstrainedDelegationAlwaysAsControlPlane UnresolvedRoleDefinitionFallbackTier = $UnresolvedRoleDefinitionFallbackTier DeletedPrincipalAssignmentHandling = $DeletedPrincipalAssignmentHandling } WorkflowTrigger = [ordered]@{ PullScheduledTrigger = $true PullScheduledCron = "30 9 * * *" PushAfterPullWorkflowTrigger = $true PushReportingAfterPullWorkflowTrigger = $true PushReportingScheduledTrigger = $false PushReportingScheduledCron = "0 9 * * 1" } AutomatedControlPlaneScopeUpdate = [ordered]@{ ApplyAutomatedControlPlaneScopeUpdate = $ApplyAutomatedControlPlaneScopeUpdate PrivilegedObjectClassificationSource = ("EntraOps", "PrivilegedRolesFromAzGraph", "PrivilegedEdgesFromExposureManagement") EntraOpsScopes = @($RbacSystems) ClassificationParameterScope = $($ClassificationParameterScope) AzureHighPrivilegedRoles = ("Owner", "Role Based Access Control Administrator", "User Access Administrator") AzureHighPrivilegedScopes = ("/", "/providers/microsoft.management/managementgroups/$($TenantId)") ExposureCriticalityLevel = "<1" } AutomatedClassificationUpdate = [ordered]@{ ApplyAutomatedClassificationUpdate = $ApplyAutomatedClassificationUpdate Classifications = ("All") } GeneratedArtifactValidation = [ordered]@{ # Contradictory tier pairs come from Custom Security Attribute drift on the source object, # so they are reported as warnings by default instead of blocking the whole collection run. FailOnContradictoryTierPair = $FailOnContradictoryTierPair FailOnPrivilegedAssignmentWithoutClassification = $FailOnPrivilegedAssignmentWithoutClassification } AutomatedEntraOpsUpdate = [ordered]@{ ApplyAutomatedEntraOpsUpdate = $ApplyAutomatedEntraOpsUpdate UpdateScheduledTrigger = $ApplyAutomatedEntraOpsUpdate UpdateScheduledCron = "0 9 * * 3" ValidationFrequency = $ValidationFrequency RunBrowserTests = $RunBrowserTests # Distribution repositories are declared once in EntraOpsUpdateContract.json: "EntraOps" # (public release channel, no credentials) or "EntraOps-Insiders" (private, requires the # EntraOpsUpdatePat secret). A different source is accepted only when it publishes a # matching EntraOpsUpdateContract.json. Repository = $UpdateRepository # Track main by default so enabled automated updates require no release-ref maintenance. Branch = "main" # GitHub workflow definitions are intentionally opt-in because GITHUB_TOKEN cannot push # them. Azure DevOps configurations instead include their pipeline templates by default. TargetUpdateFolders = $DefaultUpdateTargets PublicationMode = $DefaultUpdatePublicationMode } LogAnalytics = [ordered]@{ IngestToLogAnalytics = $IngestToLogAnalytics DataCollectionRuleName = "Enter Data Collection Rule Name and run New-EntraOpsWorkloadIdentity to assign permissions" DataCollectionRuleSubscriptionId = "Enter Subscription Id of Data Collection Rule and run New-EntraOpsWorkloadIdentity to assign permissions" DataCollectionResourceGroupName = "Enter Resource Group Name of Data Collection Rule and run New-EntraOpsWorkloadIdentity to assign permissions" TableName = "PrivilegedEAM_CL" } SentinelWatchLists = [ordered]@{ IngestToWatchLists = $IngestToWatchLists WatchListTemplates = $WatchListTemplates WatchListWorkloadIdentity = $WatchListWorkloadIdentity SentinelWorkspaceName = "Enter Log Analytics Workspace Name and run New-EntraOpsWorkloadIdentity to assign permissions" SentinelSubscriptionId = "Enter Subscription Id of Log Analytics Workspace and run New-EntraOpsWorkloadIdentity to assign permissions" SentinelResourceGroupName = "Enter Resource Group of Log Analytics Workspace and run New-EntraOpsWorkloadIdentity to assign permissions" WatchListPrefix = "EntraOps_" } AutomatedAdministrativeUnitManagement = [ordered]@{ ApplyAdministrativeUnitAssignments = $ApplyAdministrativeUnitAssignments ApplyToAccessTierLevel = ("ControlPlane", "ManagementPlane") FilterObjectType = ("User", "Group") RbacSystems = ("EntraID", "IdentityGovernance", "ResourceApps", "DeviceManagement") RestrictedAuMode = "selected" RemovalSafetyThreshold = $RemovalSafetyThreshold } AutomatedConditionalAccessTargetGroups = [ordered]@{ ApplyConditionalAccessTargetGroups = $ApplyConditionalAccessTargetGroups AdminUnitName = "Tier0-ControlPlane.ConditionalAccess" ApplyToAccessTierLevel = ("ControlPlane", "ManagementPlane") FilterObjectType = ("User", "Group") GroupPrefix = "sug_Entra.CA.IncludeUsers.PrivilegedAccounts." RbacSystems = ("EntraID", "IdentityGovernance", "ResourceApps", "DeviceManagement") RemovalSafetyThreshold = $RemovalSafetyThreshold } AutomatedRmauAssignmentsForUnprotectedObjects = [ordered]@{ ApplyRmauAssignmentsForUnprotectedObjects = $ApplyRmauAssignmentsForUnprotectedObjects ApplyToAccessTierLevel = ("ControlPlane", "ManagementPlane") FilterObjectType = ("User", "Group") RbacSystems = ("EntraID", "IdentityGovernance", "DeviceManagement") IncludeUnprotectedDevices = $IncludeUnprotectedDevices RemovalSafetyThreshold = $RemovalSafetyThreshold } AutomatedReportingGeneration = [ordered]@{ ApplyAutomatedReportingGeneration = $ApplyAutomatedReportingGeneration PublishReportsAsRelease = $PublishReportsAsRelease RunGeneratedReportTests = $RunGeneratedReportTests UploadReportTestDiagnostics = $UploadReportTestDiagnostics ReportingReleasesToKeep = $ReportingReleasesToKeep GenerateClassificationExplorer = $true GenerateTierBreachAnalyzer = $true GenerateEamDashboard = $true GenerateAccessPathMap = $true GenerateConfigurationAnalyzer = $true GenerateAccessPackageFlow = $true GeneratePrivilegeHistory = $true GeneratePrivilegedAssets = $true ClassificationExplorerRepository = "Cloud-Architekt/AzurePrivilegedIAM" } ConfigurationAnalyzer = [ordered]@{ ResolveGroupMembersForPrivilegedAssets = $ResolveGroupMembersForPrivilegedAssets AllowPartialTenantGovernanceSnapshot = $AllowPartialTenantGovernanceSnapshot MaxTenantGovernanceSnapshotAgeHours = $MaxTenantGovernanceSnapshotAgeHours StaleTenantGovernanceSnapshotAction = $StaleTenantGovernanceSnapshotAction TimeRangeInDays = $ConfigurationAnalyzerTimeRangeInDays SnapshotInterval = $ConfigurationAnalyzerSnapshotInterval PimRequestFlowExcludedRiskFlags = @($PimRequestFlowExcludedRiskFlags) AccessPackageFlowExcludedRiskFlags = @($AccessPackageFlowExcludedRiskFlags) ConditionalAccessAnalysisExcludedFindings = @($ConditionalAccessAnalysisExcludedFindings) EidscaExcludedFindings = @($EidscaExcludedFindings) } AutomatedElmCatalogProtection = [ordered]@{ ApplyPrivilegedElmCatalogProtection = $ApplyPrivilegedElmCatalogProtection ApplyToAccessTierLevel = , "ControlPlane" RemovalSafetyThreshold = $RemovalSafetyThreshold } CustomSecurityAttributes = [ordered]@{ Enabled = $EnableCustomSecurityAttributeClassification PrivilegedUserAttribute = "privilegedUser" PrivilegedUserPawAttribute = "associatedSecureAdminWorkstation" PrivilegedServicePrincipalAttribute = "privilegedWorkloadIdentity" UserWorkAccountAttribute = "associatedWorkAccount" PrivilegedUserAdminTierLevelAttribute = "adminTierLevel" PrivilegedUserAdminTierLevelNameAttribute = "adminTierLevelName" PrivilegedServicePrincipalAdminTierLevelAttribute = "adminTierLevel" PrivilegedServicePrincipalAdminTierLevelNameAttribute = "adminTierLevelName" } AlternateObjectTierLevelAttributes = [ordered]@{ User = [ordered]@{ Enabled = $EnableAlternateObjectTierLevelAttributes ControlPlane = "" ManagementPlane = "" UserAccess = "" } ServicePrincipal = [ordered]@{ Enabled = $EnableAlternateObjectTierLevelAttributes ControlPlane = "" ManagementPlane = "" UserAccess = "" } Group = [ordered]@{ Enabled = $false ControlPlane = "" ManagementPlane = "" UserAccess = "" } } ObjectClassificationFile = [ordered]@{ Enabled = $EnableObjectClassificationFile FilePath = $ObjectClassificationFilePath } PrivilegeHistory = [ordered]@{ EnablePrivilegeHistory = $EnablePrivilegeHistory TimeRangeInDays = $PrivilegeHistoryTimeRangeInDays SnapshotInterval = $PrivilegeHistorySnapshotInterval } AccessPathMap = [ordered]@{ ResolveObjectIdsOutsidePrivilegedEAM = $AccessPathMapResolveObjectIdsOutsidePrivilegedEAM } EamDashboard = [ordered]@{ ResolveLinkedIdentityObjectIds = $EamDashboardResolveLinkedIdentityObjectIds } PrivilegedAssets = [ordered]@{ ResolveRelatedObjectIds = $PrivilegedAssetsResolveRelatedObjectIds } ClassificationExplorer = [ordered]@{ GenerateChangeHistory = $ClassificationExplorerGenerateChangeHistory } TenantGovernanceSnapshot = [ordered]@{ EnableTenantGovernanceSnapshot = $EnableTenantGovernanceSnapshot ResourcesToInclude = if ($TenantGovernanceResourcesToInclude) { $TenantGovernanceResourcesToInclude } else { (Get-EntraOpsTenantGovernanceResourceDefinition).DefaultResources } SnapshotDisplayNamePrefix = "EntraOps TG" SnapshotResourceFileNaming = $TenantGovernanceSnapshotResourceFileNaming SnapshotScheduledTrigger = $EnableTenantGovernanceSnapshot SnapshotScheduledCron = $TenantGovernanceSnapshotScheduledCron SnapshotScheduledCronComplete = $TenantGovernanceSnapshotScheduledCronComplete SnapshotScheduledCronCompleteRetry1 = $TenantGovernanceSnapshotScheduledCronCompleteRetry1 SnapshotScheduledCronCompleteRetry2 = $TenantGovernanceSnapshotScheduledCronCompleteRetry2 } ServiceEM = [ordered]@{ GovernanceModel = "Centralized" ControlPlaneDelegationGroupId = "" ControlPlaneGroupName = "PRG-Tenant-ControlPlane-IdentityOps" ManagementPlaneDelegationGroupId = "" ManagementPlaneGroupName = "PRG-Tenant-ManagementPlane-PlatformOps" AdministratorGroupId = "" DefaultAzureRegion = "" SkipCatalogOwnerAssignment = $false CreateM365Group = $false AddWorkloadPlaneAdminToUsers = $false GroupPrefix = "SG" RoleAssignableWorkloadPlaneAdmins = $false AdministrativeUnits = [ordered]@{ WorkloadPlane = "" UserAccess = "" } UpdateObjectClassificationFile = $false ConstrainedDelegation = [ordered]@{ ManagementPlane = [ordered]@{ ExcludedRoleDefinitionIds = @( "8e3af657-a8ff-443c-a75c-2fe8c4bcb635" # Owner "18d7d88d-d35e-4fb5-a5c3-7773c20a72d9" # User Access Administrator "f58310d9-a9f6-439a-9e8d-f62e7b41a168" # Role Based Access Control Administrator ) AllowedTargetGroupFilter = "WorkloadPlane-Admins" } WorkloadPlane = [ordered]@{ AllowedRoleDefinitionIds = @( # Key Vault roles "00482a5a-887f-4fb3-b363-3b7fe8e74483" # Key Vault Administrator "a4417e6f-fecd-4de8-b567-7b0420556985" # Key Vault Certificates Officer "14b46e9e-c2b7-41b4-b07b-48a6ebf60603" # Key Vault Crypto Officer "12338af0-0e69-4776-bea7-57ae8d297424" # Key Vault Crypto User "21090545-7ca7-4776-b22c-e363652d74d2" # Key Vault Reader "b86a8fe4-44ce-4948-aee5-eccb2c155cd7" # Key Vault Secrets Officer "4633458b-17de-408a-b874-0445c86b69e6" # Key Vault Secrets User # Storage roles "ba92f5b4-2d11-453d-a403-e96b0029c9fe" # Storage Blob Data Contributor "b7e6dc6d-f1e8-4753-8033-0f276bb0955b" # Storage Blob Data Owner "2a2b9908-6ea1-4ae2-8e65-a410df84e7d1" # Storage Blob Data Reader "0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3" # Storage Table Data Contributor "76199698-9eea-4c19-bc75-cec21354c6b6" # Storage Table Data Reader "974c5e8b-45b9-4653-ba55-5f855dd0fb88" # Storage Queue Data Contributor "19e7f393-937e-4f77-808e-94535e297925" # Storage Queue Data Reader "8a0f0c08-91a1-4084-bc3d-661d67233fed" # Storage Queue Data Message Processor "c6a89b2d-59bc-44d0-9896-0f6e12d7b80a" # Storage Queue Data Message Sender ) AllowedTargetGroupFilter = "WorkloadPlane-Users" } } PIMAuthenticationContext = [ordered]@{ EnableAuthenticationContext = $false ControlPlane = [ordered]@{ AuthenticationContextClassReferenceId = "" AuthenticationContextDisplayName = "" } ManagementPlane = [ordered]@{ AuthenticationContextClassReferenceId = "" AuthenticationContextDisplayName = "" } WorkloadPlane = [ordered]@{ AuthenticationContextClassReferenceId = "" AuthenticationContextDisplayName = "" } } PIMForGroups = [ordered]@{ MaximumActivationDuration = "PT10H" MaximumActiveAssignmentDuration = "P15D" } AssignmentPolicies = [ordered]@{ BaselinePolicy = [ordered]@{ Expiration = "P365D"; ApprovalTimeout = "P2D"; AllowExtension = $true } WorkloadPlaneUsers = [ordered]@{ Expiration = "P365D"; ApprovalTimeout = "P2D"; RequestorScope = "AllMemberUsers"; AllowExtension = $true } WorkloadPlaneAdmins = [ordered]@{ Expiration = "P365D"; ApprovalTimeout = "P2D"; AllowExtension = $true } ManagementPlaneAdmins = [ordered]@{ Expiration = "P365D"; ApprovalTimeout = "P1D"; AllowExtension = $true } InitialWorkloadMembership = [ordered]@{ Expiration = "P365D" } InitialManagementAdmins = [ordered]@{ Expiration = "P365D" } InitialWorkloadUsers = [ordered]@{ Expiration = "P365D" } InitialWorkloadAdmins = [ordered]@{ Expiration = "P365D" } InitialCatalogMembers = [ordered]@{ Expiration = "P365D" } } AccessReviews = [ordered]@{ EnableAccessReviews = $true RecurrenceIntervalInMonths = 3 StartAfterDays = 4 ReviewDuration = "P25D" Policies = [ordered]@{ BaselinePolicy = [ordered]@{ ReviewerType = "Group"; Reviewers = @("ManagementPlane-Admins") } WorkloadPlaneUsers = [ordered]@{ ReviewerType = "Group"; Reviewers = @("WorkloadPlane-Admins") } WorkloadPlaneAdmins = [ordered]@{ ReviewerType = "Group"; Reviewers = @("ManagementPlane-Admins") } ManagementPlaneAdmins = [ordered]@{ ReviewerType = "Group"; Reviewers = @("ControlPlane-Admins") } InitialWorkloadMembership = [ordered]@{ ReviewerType = "Group"; Reviewers = @("ManagementPlane-Admins") } InitialManagementAdmins = [ordered]@{ ReviewerType = "Group"; Reviewers = @("ControlPlane-Admins") } InitialWorkloadUsers = [ordered]@{ ReviewerType = "Group"; Reviewers = @("WorkloadPlane-Admins") } InitialWorkloadAdmins = [ordered]@{ ReviewerType = "Group"; Reviewers = @("ManagementPlane-Admins") } InitialCatalogMembers = [ordered]@{ ReviewerType = "Group"; Reviewers = @("ManagementPlane-Admins") } } } } } #endregion #region Write configuration file to disk try { Write-Output "Writing configuration file to $($ConfigFilePath)..." $EnvConfigSchema | ConvertTo-Json -Depth 10 | Out-File -Path $($ConfigFilePath) } catch { Write-Error "Failed to write configuration file to $($ConfigFilePath). Error: $_" } #endregion } # SIG # Begin signature block # MIIniAYJKoZIhvcNAQcCoIIneTCCJ3UCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAJv431t+okLrE+ # otIGcOZ1nTCbMs7ZXd29TmrRw2IPH6CCIDwwggXJMIIEsaADAgECAhAbtY8lKt8j # 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 # ARUwLwYJKoZIhvcNAQkEMSIEIO/hmKGHaLJrXOe2VWlYCYVivZK4RZT4PEltoVRr # XorZMA0GCSqGSIb3DQEBAQUABIIBgCUj7aGRxtS5MlkdNFOZQ03oRqYOBRMIrfok # 9Y6f+4ujWvDW2GHBqob1gOKRgrYVXTzH8pOsOX5+XXq5K1hhZBVAck30/q526jTW # REmsZGEIjluT5eaMd3B3khAQWsS8VLONLswGYvsWRkzpuetf/9WslFndcyGsNMdM # YAslwwZffSXANOl2iMG21/Xe+PnEvpkc++6ipUl/Ay13u0/qjp7vAM7pqWxE+3gY # LxPbTO8qNdvlh3cPkomrV3UBgmKmCoCOt83MmKnYAoTXuBnIDYyaVudCb6hAExuf # bTgJwDDzScKPOeZ2Argqg008/SfjXqUFvFNNMulL13mw5Q9BIrT6m4EyT9BfWCgv # f2A79BCLpQyJGFvdu0xZsTDSCYBi38ihXtbuFqWffB/aWJrE0rQ3Jv2Fudrgy4qf # MLAym6ZxWXScLOFiZCAgNA0YpkxtiVn6w8nUiWpU4EP7d2UP8itLvc9D46vRkieX # VmutjTKQKAYMW9Sntz01OUpbz2VTiqGCBAIwggP+BgkqhkiG9w0BCQYxggPvMIID # 6wIBATBqMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0 # ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBUaW1lc3RhbXBpbmcgMjAyMSBDQQIQ # KPB3wRw2vf5fdDJHcCcuAzANBglghkgBZQMEAgIFAKCCAVYwGgYJKoZIhvcNAQkD # MQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNjEwMjcwNTM5MjZaMDcG # CyqGSIb3DQEJEAIvMSgwJjAkMCIEIIW+kOEK0kONfMkotq9IsJqyCBd87PiwEmxY # 05EFJcQ8MD8GCSqGSIb3DQEJBDEyBDCv04krO4Suu3F4jnwj/9RKq/2oqk/hiGAZ # mmUQY0maxcMf+zxHBa82kUB1Ws08HGMwgZ8GCyqGSIb3DQEJEAIMMYGPMIGMMIGJ # MIGGBBRXFGhBDKha80JO+RZKUTYQ9NONmDBuMFqkWDBWMQswCQYDVQQGEwJQTDEh # MB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMSQwIgYDVQQDExtDZXJ0 # dW0gVGltZXN0YW1waW5nIDIwMjEgQ0ECECjwd8EcNr3+X3QyR3AnLgMwDQYJKoZI # hvcNAQEBBQAEggIAW/SyEjCOqqwViK+NrUb2N8XR6sSy/k6yuGmHHhISQiglCvys # IZmlAQt66ru3RdEuCW1vIGu4tzCI3GlM4QIuzhqQFyZHK92FbRzPZZNM8gKdLj7P # Qmtkv8MEjzVavTiHBEHEesC3QIdNNyWHKmSsMy0swBHS2583VDzR5SwRjUMb193A # PivPBy1EI6g7lEHRES+E36SuhnxtGfNUVQRlMmvndBrSzB8zYtQqC7m2FQpFTlFo # QQ+xPQfqLovpwI+/Sxg/LmobRu4a52JCiq6d/2Uapz77lHt+OlqMPa8VgwuBQji/ # +L8N8V1sfT0OUO2lyP2xPWmcAfwFPKPqZ2d9VhvOnEYebwMVZ/9dsxCVjYNer4tO # l4fzA/zxezzwd31xh5G0W9KEUB+XkCF05aLXjQq6eIwBbJ8QTK8cjGufJ+WG+TOy # M9zDwnb/CQblMdcPpcOTGnJx86XYru2eqHN0XOSVi42EHRS0OCck6sdafQWIpXLn # edgtJ4n5BkYY/oqMp2IUoTnunZkw4jnnzoFAMX3tmb+b6TwZHu5IWd1CFw6WxAug # K4Dqurs39Mb4m91Tivjz+Tfht3VI8isdqGqSRv78G0dZOIQBfPlrr5nt6Rpkp4oQ # qOVL5KqPiOhKEycMpt2zS2oM1uimdKr0IhdyofQIOi3ZA218LzZtZ7Sn+aM= # SIG # End signature block |