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 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. Default and support is currently limited to GitHub. .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 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. .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 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 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 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 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 EnableAlternateObjectTierLevelAttributes Defines if User and ServicePrincipal objects should be classified by PowerShell filter expressions evaluated against their own resolved EntraOps details (section AlternateObjectTierLevelAttributes) instead of Custom Security Attributes. Default is false. The config file always includes the section with empty filter expressions so it can be filled in and enabled later; see README.md "Classify privileged objects by Alternate Tier Level Attributes" for the required syntax. .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. .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 #> 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)] [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)] [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]$ClassificationExplorerGenerateChangeHistory = $false, [Parameter(Mandatory = $false)] [boolean]$ResolveGroupMembersForPrivilegedAssets = $true, [Parameter(Mandatory = $false)] [boolean]$AllowPartialTenantGovernanceSnapshot = $true, [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]$EnableAlternateObjectTierLevelAttributes = $false, [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" #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 $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) 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" # Workflow definitions are intentionally opt-in: the built-in GITHUB_TOKEN cannot push # changes below .github/workflows. Add the target explicitly only for an interactive # update or after configuring the dedicated publisher GitHub App documented in core.md. TargetUpdateFolders = @("./.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") PublicationMode = $UpdatePublicationMode } 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 ReportingReleasesToKeep = $ReportingReleasesToKeep GenerateClassificationExplorer = $true GenerateTierBreachAnalyzer = $true GenerateEamDashboard = $true GenerateAccessPathMap = $true GenerateConfigurationAnalyzer = $true GenerateAccessPackageFlow = $true GeneratePrivilegeHistory = $true ClassificationExplorerRepository = "Cloud-Architekt/AzurePrivilegedIAM" } ConfigurationAnalyzer = [ordered]@{ ResolveGroupMembersForPrivilegedAssets = $ResolveGroupMembersForPrivilegedAssets AllowPartialTenantGovernanceSnapshot = $AllowPartialTenantGovernanceSnapshot PimRequestFlowExcludedRiskFlags = @($PimRequestFlowExcludedRiskFlags) AccessPackageFlowExcludedRiskFlags = @($AccessPackageFlowExcludedRiskFlags) ConditionalAccessAnalysisExcludedFindings = @($ConditionalAccessAnalysisExcludedFindings) EidscaExcludedFindings = @($EidscaExcludedFindings) } AutomatedElmCatalogProtection = [ordered]@{ ApplyPrivilegedElmCatalogProtection = $ApplyPrivilegedElmCatalogProtection ApplyToAccessTierLevel = , "ControlPlane" RemovalSafetyThreshold = $RemovalSafetyThreshold } CustomSecurityAttributes = [ordered]@{ PrivilegedUserAttribute = "privilegedUser" PrivilegedUserPawAttribute = "associatedSecureAdminWorkstation" PrivilegedServicePrincipalAttribute = "privilegedWorkloadIdentity" UserWorkAccountAttribute = "associatedWorkAccount" PrivilegedUserAdminTierLevelAttribute = "adminTierLevel" PrivilegedUserAdminTierLevelNameAttribute = "adminTierLevelName" PrivilegedServicePrincipalAdminTierLevelAttribute = "adminTierLevel" PrivilegedServicePrincipalAdminTierLevelNameAttribute = "adminTierLevelName" } AlternateObjectTierLevelAttributes = [ordered]@{ Enabled = $EnableAlternateObjectTierLevelAttributes User = [ordered]@{ ControlPlane = "" ManagementPlane = "" UserAccess = "" } ServicePrincipal = [ordered]@{ ControlPlane = "" ManagementPlane = "" UserAccess = "" } } PrivilegeHistory = [ordered]@{ EnablePrivilegeHistory = $EnablePrivilegeHistory TimeRangeInDays = $PrivilegeHistoryTimeRangeInDays SnapshotInterval = $PrivilegeHistorySnapshotInterval } AccessPathMap = [ordered]@{ ResolveObjectIdsOutsidePrivilegedEAM = $AccessPathMapResolveObjectIdsOutsidePrivilegedEAM } EamDashboard = [ordered]@{ ResolveLinkedIdentityObjectIds = $EamDashboardResolveLinkedIdentityObjectIds } 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 } } #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 # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAgw5OCk+XSck3u # svVW4A2L+Dzni4/wMjLoFs8SqVIQz6CCIDwwggXJMIIEsaADAgECAhAbtY8lKt8j # 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 # ARUwLwYJKoZIhvcNAQkEMSIEINGwoE61ziwM/mdtFdJKbg+Rjw1h1hT+Xhmu0yoo # /F4mMA0GCSqGSIb3DQEBAQUABIIBgCijnKPvMM4bG5CayI0R0BmpR0gRnsezNWMz # HgUi7sxLawuxWnIYU6MYQGlrrSchwbjCdjfLXPjJ4CZKbWpngnlWWhpNyBOWKSHK # Nm1nyrcBWEoW4XEiyalUKKsZFGK/TrxN+Fyn9Es2dbqdfc/eixtnDSwQhhFrwRDc # h0ui6w5ZyshGqHjzDT/G0OtbZPWDXocL973TNo0OrzpGrk2fcADOqbgiCf/GF38Z # Yh2ouDY9FrU09EclQkB0djcih8TRAZlVjJZLPaNqZjhjbsEnm+Gt/FRVtDjPh2QD # s0tQAQaxyywNxZZRTRSayU6NQJmmdyps39y4tdzX1rvn2edzGKjrlFhlinnnAFd1 # 4zo07Y0CONsf7caDxR/sW21JMLtDYfDl0vexb7eJlDPsJLWfop/tIkCSjRtqrYFU # 7uP45wtWGxhfgchWiGqQChFjHzKJ96R0+BWU+sdVFK8QsgzgYWAg50pPWOBQ6Iaf # s6ohE3ROepzXyG+uxNxGEF5dX8lIAKGCBAIwggP+BgkqhkiG9w0BCQYxggPvMIID # 6wIBATBqMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0 # ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBUaW1lc3RhbXBpbmcgMjAyMSBDQQIQ # KPB3wRw2vf5fdDJHcCcuAzANBglghkgBZQMEAgIFAKCCAVYwGgYJKoZIhvcNAQkD # MQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNjEwMTMwOTQwMzJaMDcG # CyqGSIb3DQEJEAIvMSgwJjAkMCIEIIW+kOEK0kONfMkotq9IsJqyCBd87PiwEmxY # 05EFJcQ8MD8GCSqGSIb3DQEJBDEyBDAV3vQ4guK643SZE5MHxFks+RN2mZDnRXAL # IRizmsmfR+TRQ68P9+blgo72bbGbV2owgZ8GCyqGSIb3DQEJEAIMMYGPMIGMMIGJ # MIGGBBRXFGhBDKha80JO+RZKUTYQ9NONmDBuMFqkWDBWMQswCQYDVQQGEwJQTDEh # MB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMSQwIgYDVQQDExtDZXJ0 # dW0gVGltZXN0YW1waW5nIDIwMjEgQ0ECECjwd8EcNr3+X3QyR3AnLgMwDQYJKoZI # hvcNAQEBBQAEggIABlFyWJIjev4Af+U2CQxg2Hpz/Ka5ygDTf6qg0PCIypjmLyIN # HHFcnRFRvxvtLpY2GaLKJhxwSjKDwAQaAxgdug5g4k6rRLb9a00RYYS6bcnyhMJ7 # 0E1ZjPll5dJLTQZZsYBB0J0PLGCiS2sLCd/6mZC7fclMiPWJW85ClyfrjBAattgm # v1ngoRB1ainQz8vXYyUjCZ7xnvwUUUE/vyDO4hXcY3VBVcBkFOwt0qjDg0rdttv5 # Y1/FszaeVZBA0WPBBKjYbxJ9J3YolFfCZIJW0uPxPd8pr2f3NYqLy4eFBuZv28+A # BWbBTt7v8d+HpY+sHfYPKmudFiZX6oWmUdWQdUnBI2Qk6+Sq4jvonbMIdrjrHa8L # 1b4n/DYXgnfFV0geWc8lN4L7rQOCGkShpqzVOR7ZXbAoh6W1uLl29txLK6ya/DwC # fAenDFsrt3MJPp9YeKPx86ge6wMFthpOwvRRmkN87MiX9KYP1Uy/361/EFpMXLJt # oQbB9p6OCRaHyGAbEIOwXQnA/DlRS1QcSB/wS8WpwReDXBxY/F0G/DviCi5AnzH/ # OL/CHJ7zFZ97XiBKfz2mlK1N7niyFEMIZ7nlpGJmAhoKRLQl5ssv8CwkJgtwyZW+ # PwtwZE3ZMdX2vJ9hKdVnCqlm3NmB1Rm6arZoytKgxA0zVIyuoGSGXYo6uBE= # SIG # End signature block |