EntraExporter.psm1
#Requires -Version 5.1 #Requires -PSEdition Core,Desktop #Requires -Module @{'GUID'='883916f2-9184-46ee-b1f8-b6a2fb784cee';'ModuleVersion'='2.2.0';'ModuleName'='Microsoft.Graph.Authentication'} <# .SYNOPSIS EntraExporter .DESCRIPTION This module exports an Entra tenant's identity related configuration settings and objects and writes them to json files. .NOTES ModuleVersion: 2.0.5 GUID: d6c15273-d343-4556-a30d-b333eca3c1ab Author: Microsoft Identity CompanyName: Microsoft Corporation Copyright: Microsoft Corporation. All rights reserved. .FUNCTIONALITY Connect-EntraExporter, Export-Entra .LINK https://github.com/microsoft/entraexporter #> #region NestedModules Script(s) #region Invoke-Graph.ps1 <# .SYNOPSIS Run a Microsoft Graph Command #> function Invoke-Graph{ [CmdletBinding()] param( # Graph endpoint such as "users". [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [string[]] $RelativeUri, # Specifies unique Id(s) for the URI endpoint. For example, users endpoint accepts Id or UPN. [Parameter(Mandatory = $false)] [string[]] $UniqueId, # Filters properties (columns). [Parameter(Mandatory = $false)] [string[]] $Select, # Filters results (rows). https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter [Parameter(Mandatory = $false)] [string] $Filter, # Parameters such as "$top". [Parameter(Mandatory = $false)] [hashtable] $QueryParameters, # API Version. [Parameter(Mandatory = $false)] [ValidateSet('v1.0', 'beta')] [string] $ApiVersion = 'v1.0', # Specifies consistency level. [Parameter(Mandatory = $false)] [string] $ConsistencyLevel = 'eventual', # Only return first page of results. [Parameter(Mandatory = $false)] [switch] $DisablePaging, # Force individual requests to MS Graph. [Parameter(Mandatory = $false)] [switch] $DisableBatching, # Specify Batch size. [Parameter(Mandatory = $false)] [int] $BatchSize = 20, # Base URL for Microsoft Graph API. [Parameter(Mandatory = $false)] [uri] $GraphBaseUri ) begin { if(!$GraphBaseUri){ if(!(Test-Path variable:global:GraphBaseUri)){ $global:GraphBaseUri = $((Get-MgEnvironment -Name (Get-MgContext).Environment).GraphEndpoint) } $GraphBaseUri = $global:GraphBaseUri } $listRequests = New-Object 'System.Collections.Generic.List[psobject]' function Format-Result ($results, $RawOutput) { if (!$RawOutput -and $results -and (Get-ObjectProperty $results 'value')) { foreach ($result in $results.value) { if ($result -is [hashtable]) { $result.Add('@odata.context', ('{0}/$entity' -f $results.'@odata.context')) } else { $result | Add-Member -MemberType NoteProperty -Name '@odata.context' -Value ('{0}/$entity' -f $results.'@odata.context') } Write-Output $result } } else { Write-Output $results } } function Complete-Result ($results, $DisablePaging) { if (!$DisablePaging -and $results) { while (Get-ObjectProperty $results '@odata.nextLink') { $results = Invoke-MgGraphRequest -Method GET -Uri $results.'@odata.nextLink' -Headers @{ ConsistencyLevel = $ConsistencyLevel } -OutputType PSObject Format-Result $results $DisablePaging } } } } process { ## Initialize $results = $null if (!$UniqueId) { [string[]] $UniqueId = '' } if ($DisableBatching -and ($RelativeUri.Count -gt 1 -or $UniqueId.Count -gt 1)) { Write-Warning ('This command is invoking {0} individual Graph requests. For better performance, remove the -DisableBatching parameter.' -f ($RelativeUri.Count * $UniqueId.Count)) } ## Process Each RelativeUri foreach ($uri in $RelativeUri) { $uriQueryEndpoint = New-Object System.UriBuilder -ArgumentList ([IO.Path]::Combine($GraphBaseUri.AbsoluteUri, $ApiVersion, $uri)) ## Combine query parameters from URI and cmdlet parameters if ($uriQueryEndpoint.Query) { [hashtable] $finalQueryParameters = ConvertFrom-QueryString $uriQueryEndpoint.Query -AsHashtable if ($QueryParameters) { foreach ($ParameterName in $QueryParameters.Keys) { $finalQueryParameters[$ParameterName] = $QueryParameters[$ParameterName] } } } elseif ($QueryParameters) { [hashtable] $finalQueryParameters = $QueryParameters } else { [hashtable] $finalQueryParameters = @{ } } if ($Select) { $finalQueryParameters['$select'] = $Select -join ',' } if ($Filter) { $finalQueryParameters['$filter'] = $Filter } $uriQueryEndpoint.Query = ConvertTo-QueryString $finalQueryParameters ## Invoke graph requests individually or save for single batch request foreach ($id in $UniqueId) { $uriQueryEndpointFinal = New-Object System.UriBuilder -ArgumentList $uriQueryEndpoint.Uri $uriQueryEndpointFinal.Path = ([IO.Path]::Combine($uriQueryEndpointFinal.Path, $id)) if (!$DisableBatching -and ($RelativeUri.Count -gt 1 -or $UniqueId.Count -gt 1)) { ## Create batch request entry $request = New-Object PSObject -Property @{ id = $listRequests.Count #(New-Guid).ToString() method = 'GET' url = $uriQueryEndpointFinal.Uri.AbsoluteUri -replace ('{0}{1}/' -f $GraphBaseUri.AbsoluteUri, $ApiVersion) headers = @{ ConsistencyLevel = $ConsistencyLevel } } $listRequests.Add($request) } else { ## Get results $results = Invoke-MgGraphRequest -Method GET -Uri $uriQueryEndpointFinal.Uri.AbsoluteUri -Headers @{ ConsistencyLevel = $ConsistencyLevel } -OutputType PSObject Format-Result $results $DisablePaging Complete-Result $results $DisablePaging } } } } end { if ($listRequests.Count -gt 0) { $uriQueryEndpoint = New-Object System.UriBuilder -ArgumentList ([IO.Path]::Combine($GraphBaseUri.AbsoluteUri, $ApiVersion, '$batch')) for ($iRequest = 0; $iRequest -lt $listRequests.Count; $iRequest += $BatchSize) { $indexEnd = [System.Math]::Min($iRequest + $BatchSize - 1, $listRequests.Count - 1) $jsonRequests = New-Object psobject -Property @{ requests = $listRequests[$iRequest..$indexEnd] } | ConvertTo-Json -Depth 5 Write-Debug $jsonRequests $resultsBatch = Invoke-MgGraphRequest -Method POST -Uri $uriQueryEndpoint.Uri.AbsoluteUri -Body $jsonRequests -OutputType PSObject $resultsBatch = $resultsBatch.responses | Sort-Object -Property id foreach ($results in ($resultsBatch.body)) { Format-Result $results $DisablePaging Complete-Result $results $DisablePaging } } } } } #endregion #region Get-ObjectProperty.ps1 <# .SYNOPSIS Get object property value. .EXAMPLE PS C:\>$object = New-Object psobject -Property @{ title = 'title value' } PS C:\>$object | Get-ObjectProperty -Property 'title' Get value of object property named title. .EXAMPLE PS C:\>$object = New-Object psobject -Property @{ lvl1 = (New-Object psobject -Property @{ nextLevel = 'lvl2 data' }) } PS C:\>Get-ObjectProperty $object -Property 'lvl1', 'nextLevel' Get value of nested object property named nextLevel. .INPUTS System.Collections.Hashtable System.Management.Automation.PSObject #> function Get-ObjectProperty { [CmdletBinding()] [OutputType([object])] param ( # Object containing property values [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [object] $InputObjects, # Name of property. Specify an array of property names to tranverse nested objects. [Parameter(Mandatory = $true, ValueFromRemainingArguments = $true)] [string[]] $Property ) process { foreach ($InputObject in $InputObjects) { for ($iProperty = 0; $iProperty -lt $Property.Count; $iProperty++) { ## Get property value if ($InputObject -is [hashtable]) { if ($InputObject.ContainsKey($Property[$iProperty])) { $PropertyValue = $InputObject[$Property[$iProperty]] } else { $PropertyValue = $null} } else { $PropertyValue = Select-Object -InputObject $InputObject -ExpandProperty $Property[$iProperty] -ErrorAction SilentlyContinue } ## Check for more nested properties if ($iProperty -lt $Property.Count - 1) { $InputObject = $PropertyValue if ($null -eq $InputObject) { break } } else { Write-Output $PropertyValue } } } } } #endregion #region ConvertTo-OrderedDictionary.ps1 function ConvertTo-OrderedDictionary { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] $InputObject ) process { if($InputObject){ if($InputObject -is [array]){ $outputArray = @() foreach($item in $InputObject){ $outputArray += ConvertTo-OrderedDictionary $item } return $outputArray } elseif($InputObject -is [hashtable]){ $outputObject = [ordered]@{} foreach ($Item in ($InputObject.GetEnumerator() | Sort-Object -Property Key)) { if($Item){ $value = Get-ObjectProperty $Item 'Value' if($value -is [hashtable] -or $value -is [array]){ #if child is a hashtable or array, sort it too $Item.Value = ConvertTo-OrderedDictionary $value } } $outputObject[$Item.Key] = $Item.Value } return $outputObject } } else { return $InputObject } } } #endregion #region ConvertFrom-QueryString.ps1 <# .SYNOPSIS Convert Query String to object. .EXAMPLE PS C:\>ConvertFrom-QueryString '?name=path/file.json&index=10' Convert query string to object. .EXAMPLE PS C:\>'name=path/file.json&index=10' | ConvertFrom-QueryString -AsHashtable Convert query string to hashtable. .INPUTS System.String .LINK https://github.com/jasoth/Utility.PS #> function ConvertFrom-QueryString { [CmdletBinding()] [OutputType([psobject])] [OutputType([hashtable])] param ( # Value to convert [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [string[]] $InputStrings, # URL decode parameter names [Parameter(Mandatory = $false)] [switch] $DecodeParameterNames, # Converts to hash table object [Parameter(Mandatory = $false)] [switch] $AsHashtable ) process { foreach ($InputString in $InputStrings) { if ($AsHashtable) { [hashtable] $OutputObject = @{ } } else { [psobject] $OutputObject = New-Object psobject } if ($InputString[0] -eq '?') { $InputString = $InputString.Substring(1) } [string[]] $QueryParameters = $InputString.Split('&') foreach ($QueryParameter in $QueryParameters) { [string[]] $QueryParameterPair = $QueryParameter.Split('=') if ($DecodeParameterNames) { $QueryParameterPair[0] = [System.Net.WebUtility]::UrlDecode($QueryParameterPair[0]) } if ($OutputObject -is [hashtable]) { $OutputObject.Add($QueryParameterPair[0], [System.Net.WebUtility]::UrlDecode($QueryParameterPair[1])) } else { $OutputObject | Add-Member $QueryParameterPair[0] -MemberType NoteProperty -Value ([System.Net.WebUtility]::UrlDecode($QueryParameterPair[1])) } } Write-Output $OutputObject } } } #endregion #region ConvertTo-QueryString.ps1 <# .SYNOPSIS Convert Hashtable to Query String. .EXAMPLE PS C:\>ConvertTo-QueryString @{ name = 'path/file.json'; index = 10 } Convert hashtable to query string. .EXAMPLE PS C:\>[ordered]@{ title = 'convert&prosper'; id = [guid]'352182e6-9ab0-4115-807b-c36c88029fa4' } | ConvertTo-QueryString Convert ordered dictionary to query string. .INPUTS System.Collections.Hashtable .LINK https://github.com/jasoth/Utility.PS #> function ConvertTo-QueryString { [CmdletBinding()] [OutputType([string])] param ( # Value to convert [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [object] $InputObjects, # URL encode parameter names [Parameter(Mandatory = $false)] [switch] $EncodeParameterNames ) process { foreach ($InputObject in $InputObjects) { $QueryString = New-Object System.Text.StringBuilder if ($InputObject -is [hashtable] -or $InputObject -is [System.Collections.Specialized.OrderedDictionary] -or $InputObject.GetType().FullName.StartsWith('System.Collections.Generic.Dictionary')) { foreach ($Item in $InputObject.GetEnumerator()) { if ($QueryString.Length -gt 0) { [void]$QueryString.Append('&') } [string] $ParameterName = $Item.Key if ($EncodeParameterNames) { $ParameterName = [System.Net.WebUtility]::UrlEncode($ParameterName) } [void]$QueryString.AppendFormat('{0}={1}', $ParameterName, [System.Net.WebUtility]::UrlEncode($Item.Value)) } } elseif ($InputObject -is [object] -and $InputObject -isnot [ValueType]) { foreach ($Item in ($InputObject | Get-Member -MemberType Property, NoteProperty)) { if ($QueryString.Length -gt 0) { [void]$QueryString.Append('&') } [string] $ParameterName = $Item.Name if ($EncodeParameterNames) { $ParameterName = [System.Net.WebUtility]::UrlEncode($ParameterName) } [void]$QueryString.AppendFormat('{0}={1}', $ParameterName, [System.Net.WebUtility]::UrlEncode($InputObject.($Item.Name))) } } else { ## Non-Terminating Error $Exception = New-Object ArgumentException -ArgumentList ('Cannot convert input of type {0} to query string.' -f $InputObject.GetType()) Write-Error -Exception $Exception -Category ([System.Management.Automation.ErrorCategory]::ParserError) -CategoryActivity $MyInvocation.MyCommand -ErrorId 'ConvertQueryStringFailureTypeNotSupported' -TargetObject $InputObject continue } Write-Output $QueryString.ToString() } } } #endregion #region Connect-EntraExporter.ps1 $global:TenantID = $null <# .SYNOPSIS Connect the Entra Exporter module to the Entra tenant. .DESCRIPTION This command will connect Microsoft.Graph to your Entra tenant. You can also directly call Connect-MgGraph if you require other options to connect Use the following scopes when authenticating with Connect-MgGraph. Connect-MgGraph -Scopes 'Directory.Read.All', 'Policy.Read.All', 'IdentityProvider.Read.All', 'Organization.Read.All', 'User.Read.All', 'EntitlementManagement.Read.All', 'UserAuthenticationMethod.Read.All', 'IdentityUserFlow.Read.All', 'APIConnectors.Read.All', 'AccessReview.Read.All', 'Agreement.Read.All', 'Policy.Read.PermissionGrant', 'PrivilegedAccess.Read.AzureResources', 'PrivilegedAccess.Read.AzureAD', 'Application.Read.All' .EXAMPLE PS C:\>Connect-EntraExporter Connect to home tenant of authenticated user. .EXAMPLE PS C:\>Connect-EntraExporter -TenantId 3043-343434-343434 Connect to a specific Tenant #> function Connect-EntraExporter { param( [Parameter(Mandatory = $false)] [string] $TenantId = 'common', [Parameter(Mandatory=$false)] [ArgumentCompleter( { param ( $CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters ) (Get-MgEnvironment).Name } )] [string]$Environment = 'Global' ) Connect-MgGraph -TenantId $TenantId -Environment $Environment -Scopes 'Directory.Read.All', 'Policy.Read.All', 'IdentityProvider.Read.All', 'Organization.Read.All', 'User.Read.All', 'EntitlementManagement.Read.All', 'UserAuthenticationMethod.Read.All', 'IdentityUserFlow.Read.All', 'APIConnectors.Read.All', 'AccessReview.Read.All', 'Agreement.Read.All', 'Policy.Read.PermissionGrant', 'PrivilegedAccess.Read.AzureResources', 'PrivilegedAccess.Read.AzureAD', 'Application.Read.All' Get-MgContext $global:TenantID = (Get-MgContext).TenantId } #endregion #region Export-Entra.ps1 <# .Synopsis Exports Entra's configuration and settings for a tenant .Description This cmdlet reads the configuration information from the target Entra tenant and produces the output files in a target directory .PARAMETER OutputDirectory Specifies the directory path where the output files will be generated. .PARAMETER Type Specifies the type of objects to export. Default to Config which exports the key configuration settings of the tenant. .PARAMETER All If specified performs a full export of all objects and configuration in the tenant. .EXAMPLE .\Export-Entra -Path 'c:\temp\contoso' Runs a default export and includes the key tenant configuration settings. Does not include large data collections such as users, static groups, applications, service principals, etc. .EXAMPLE .\Export-Entra -Path 'c:\temp\contoso' -All Runs a full export of all objects and configuration settings. .EXAMPLE .\Export-Entra -Path 'c:\temp\contoso' -All -CloudUsersAndGroupsOnly Runs a full export but excludes on-prem synced users and groups. .EXAMPLE .\Export-Entra -Path 'c:\temp\contoso' -Type ConditionalAccess, AppProxy Runs an export that includes just the Conditional Access and Application Proxy settings. .EXAMPLE .\Export-Entra -Path 'c:\temp\contoso' -Type B2C Runs an export of all B2C settings. #> Function Export-Entra { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [String]$Path, [Parameter(Mandatory = $false)] [ValidateSet('All', 'Config', 'AccessReviews', 'ConditionalAccess', 'Users', 'Groups', 'Applications', 'ServicePrincipals','B2C','B2B','PIM','PIMAzure','PIMAAD', 'AppProxy', 'Organization', 'Domains', 'EntitlementManagement', 'Policies', 'AdministrativeUnits', 'SKUs', 'Identity', 'Roles','Governance')] [String[]]$Type = 'Config', [Parameter(Mandatory = $false)] [object]$ExportSchema, [Parameter(Mandatory = $false)] [string[]]$Parents, # Performs a full export if true [Parameter(Mandatory = $false)] [switch] $All, # Excludes onPrem synced users and groups from export [Parameter(Mandatory = $false)] [switch] $CloudUsersAndGroupsOnly ) if ($null -eq (Get-MgContext)) { Write-Error "No active connection. Run Connect-EntraExporter or Connect-MgGraph to sign in and then retry." exit } if($All) {$Type = @('All')} $global:Type = $Type #Used in places like Groups where Config flag will limit the resultset to just dynamic groups. if (!$ExportSchema) { $ExportSchema = Get-EEDefaultSchema } # aditional filters foreach ($entry in $ExportSchema) { $graphUri = Get-ObjectProperty $entry "GraphUri" # filter out synced users or groups if ($CloudUsersAndGroupsOnly -and ($graphUri -in "users","groups")) { if([string]::IsNullOrEmpty($entry.Filter)){ $entry.Filter = "onPremisesSyncEnabled ne true" } else { $entry.Filter = $entry.Filter + " and (onPremisesSyncEnabled ne true)" } } # get all PIM elements if ($All -and ($graphUri -in "privilegedAccess/aadroles/resources","privilegedAccess/azureResources/resources")) { $entry.Filter = $null } } foreach ($item in $ExportSchema) { $typeMatch = Compare-Object $item.Tag $Type -ExcludeDifferent -IncludeEqual $hasParents = $Parents -and $Parents.Count -gt 0 if( ($typeMatch)) { $outputFileName = Join-Path -Path $Path -ChildPath $item.Path $spacer = '' if($hasParents) { $spacer = ''.PadRight($Parents.Count + 3, ' ') + $Parents[$Parents.Count-1] } Write-Host "$spacer $($item.Path)" $command = Get-ObjectProperty $item 'Command' $graphUri = Get-ObjectProperty $item 'GraphUri' $apiVersion = Get-ObjectProperty $item 'ApiVersion' $ignoreError = Get-ObjectProperty $item 'IgnoreError' if (!$apiVersion) { $apiVersion = 'v1.0' } $resultItems = $null if($command) { if ($hasParents){ $command += " -Parents $Parents" } $resultItems = Invoke-Expression -Command $command } else { if ($hasParents){ $graphUri = $graphUri -replace '{id}', $Parents[$Parents.Count-1] } try { $resultItems = Invoke-Graph $graphUri -Filter (Get-ObjectProperty $item 'Filter') -Select (Get-ObjectProperty $item 'Select') -QueryParameters (Get-ObjectProperty $item 'QueryParameters') -ApiVersion $apiVersion } catch { $e = "" if($_.ErrorDetails -and $_.ErrorDetails.Message) { $e = $_.ErrorDetails.Message } if($e.Contains($ignoreError) -or $e.Contains('Encountered an internal server error')){ Write-Debug $_ } else { Write-Error $_ } } } if ($outputFileName -match "\.json$") { if($resultItems){ $resultItems | ConvertTo-Json -depth 100 | Out-File (New-Item -Path $outputFileName -Force) } } else { foreach($resultItem in $resultItems) { if (!$resultItem.PSObject.Properties['id']) { continue } $itemOutputFileName = Join-Path -Path $outputFileName -ChildPath $resultItem.id $parentOutputFileName = Join-Path $itemOutputFileName -ChildPath $resultItem.id $resultItem | ConvertTo-Json -depth 100 | Out-File (New-Item -Path "$($parentOutputFileName).json" -Force) if ($item.ContainsKey('Children')) { $itemParents = $Parents $itemParents += $resultItem.Id Export-Entra -Path $itemOutputFileName -Type $Type -ExportSchema $item.Children -Parents $itemParents } } } } } } #endregion #region Get-EEDefaultSchema.ps1 <# .Synopsis Gets the default export schema definition .Description Gets the default export schema definition. Defining the order in which elements are exported. .Example Get-EEDefaultSchema #> function Get-EEDefaultSchema { $global:TenantID = (Get-MgContext).TenantId return @( # Organization @{ GraphUri = 'organization' Path = 'Organization/Organization.json' Tag = @('All', 'Config', 'Organization') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' }, @{ GraphUri = 'organization/{0}/settings' -f $TenantID Path = 'Organization/Settings.json' ApiVersion = 'beta' Tag = @('All', 'Config', 'Organization') DelegatedPermission = 'User.Read.All' }, @{ GraphUri = 'organization/{0}/branding/localizations' -f $TenantID Path = 'Organization/Branding/Localizations.json' Tag = @('All', 'Config', 'Organization') DelegatedPermission = 'User.Read.All' }, @{ GraphUri = 'organization/{0}/certificateBasedAuthConfiguration' -f $TenantID Path = 'Organization/CertificateBasedAuthConfiguration.json' Tag = @('All', 'Config', 'Organization') DelegatedPermission = 'Organization.Read.All' ApplicationPermission = 'Organization.Read.All' }, @{ GraphUri = 'domains' Path = 'Domains' Tag = @('All', 'Config','Domains') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' }, @{ GraphUri = 'identity/apiConnectors' Path = 'Identity/APIConnectors' ApiVersion = 'beta' IgnoreError = 'The feature self service sign up is not enabled for the tenant' Tag = @('All', 'Config', 'Identity') DelegatedPermission = 'APIConnectors.ReadWrite.All' ApplicationPermission = 'APIConnectors.ReadWrite.All' }, @{ GraphUri = 'identityProviders' Path = 'IdentityProviders' Tag = @('All', 'Config', 'Identity') DelegatedPermission = 'IdentityProvider.Read.All' }, @{ GraphUri = 'subscribedSkus' Path = 'SubscribedSkus' Tag = @('All', 'Config', 'SKUs') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' }, @{ GraphUri = 'directoryRoles' Path = 'DirectoryRoles' Tag = @('All', 'Config', 'Roles') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' Children = @( @{ GraphUri = 'directoryRoles/{id}/members' Select = 'id, userPrincipalName, displayName' Path = 'Members' Tag = @('All', 'Config', 'Roles') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' } @{ GraphUri = 'directoryroles/{id}/scopedMembers' Path = 'ScopedMembers' Tag = @('All', 'Config', 'Roles') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' } ) }, # B2C @{ GraphUri = 'identity/userFlows' Path = 'Identity/UserFlows' Tag = @('B2C') DelegatedPermission = 'IdentityUserFlow.Read.All' ApplicationPermission = 'IdentityUserFlow.Read.All' }, @{ GraphUri = 'identity/b2cUserFlows' Path = 'Identity/B2CUserFlows' Tag = @('B2C') DelegatedPermission = 'IdentityUserFlow.Read.All' ApplicationPermission = 'IdentityUserFlow.Read.All' Children = @( @{ GraphUri = 'identity/b2cUserFlows/{id}/identityProviders' Path = 'IdentityProviders' Tag = @('B2C') DelegatedPermission = 'IdentityUserFlow.Read.All' ApplicationPermission = 'IdentityUserFlow.Read.All' }, @{ GraphUri = 'identity/b2cUserFlows/{id}/userAttributeAssignments' QueryParameters = @{ expand = 'userAttribute' } Path = 'UserAttributeAssignments' Tag = @('B2C') DelegatedPermission = 'IdentityUserFlow.Read.All' ApplicationPermission = 'IdentityUserFlow.Read.All' }, @{ GraphUri = 'identity/b2cUserFlows/{id}/apiConnectorConfiguration' QueryParameters = @{ expand = 'postFederationSignup,postAttributeCollection' } Path = 'ApiConnectorConfiguration' Tag = @('B2C') DelegatedPermission = 'IdentityUserFlow.Read.All' ApplicationPermission = 'IdentityUserFlow.Read.All' }, @{ GraphUri = 'identity/b2cUserFlows/{id}/languages' Path = 'Languages' Tag = @('B2C') DelegatedPermission = 'IdentityUserFlow.Read.All' ApplicationPermission = 'IdentityUserFlow.Read.All' } ) }, # B2B @{ GraphUri = 'identity/userFlowAttributes' Path = 'Identity/UserFlowAttributes' ApiVersion = 'beta' Tag = @('Config', 'B2B', 'B2C') DelegatedPermission = 'IdentityUserFlow.Read.All' ApplicationPermission = 'IdentityUserFlow.Read.All' IgnoreError = 'The feature self service sign up is not enabled for the tenant' }, @{ GraphUri = 'identity/b2xUserFlows' Path = 'Identity/B2XUserFlows' ApiVersion = 'beta' Tag = @('All', 'Config', 'B2B') DelegatedPermission = 'IdentityUserFlow.Read.All' ApplicationPermission = 'IdentityUserFlow.Read.All' Children = @( @{ GraphUri = 'identity/b2xUserFlows/{id}/identityProviders' Path = 'IdentityProviders' ApiVersion = 'beta' Tag = @('All', 'Config', 'B2B') DelegatedPermission = 'IdentityUserFlow.Read.All' ApplicationPermission = 'IdentityUserFlow.Read.All' }, @{ GraphUri = 'identity/b2xUserFlows/{id}/userAttributeAssignments' QueryParameters = @{ expand = 'userAttribute' } Path = 'AttributeAssignments' ApiVersion = 'beta' Tag = @('All', 'Config', 'B2B') DelegatedPermission = 'IdentityUserFlow.Read.All' ApplicationPermission = 'IdentityUserFlow.Read.All' }, @{ GraphUri = 'identity/b2xUserFlows/{id}/apiConnectorConfiguration' QueryParameters = @{ expand = 'postFederationSignup,postAttributeCollection' } Path = 'APIConnectors' ApiVersion = 'beta' Tag = @('All', 'Config', 'B2B') DelegatedPermission = 'IdentityUserFlow.Read.All' ApplicationPermission = 'IdentityUserFlow.Read.All' }, @{ GraphUri = 'identity/b2xUserFlows/{id}/languages' Path = 'Languages' ApiVersion = 'beta' Tag = @('All', 'Config', 'B2B') DelegatedPermission = 'IdentityUserFlow.Read.All' ApplicationPermission = 'IdentityUserFlow.Read.All' } ) }, # Policies @{ GraphUri = 'policies/identitySecurityDefaultsEnforcementPolicy' Path = 'Policies/IdentitySecurityDefaultsEnforcementPolicy' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/authorizationPolicy' Path = 'Policies/AuthorizationPolicy' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/featureRolloutPolicies' Path = 'Policies/FeatureRolloutPolicies' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Directory.ReadWrite.All' }, @{ GraphUri = 'policies/activityBasedTimeoutPolicies' Path = 'Policies/ActivityBasedTimeoutPolicy' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/homeRealmDiscoveryPolicies' Path = 'Policies/HomeRealmDiscoveryPolicy' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/claimsMappingPolicies' Path = 'Policies/ClaimsMappingPolicy' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/tokenIssuancePolicies' Path = 'Policies/TokenIssuancePolicy' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/tokenLifetimePolicies' Path = 'Policies/TokenLifetimePolicy' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/authenticationMethodsPolicy/authenticationMethodConfigurations/email' Path = 'Policies/AuthenticationMethodsPolicy/AuthenticationMethodConfigurations/Email.json' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/authenticationMethodsPolicy/authenticationMethodConfigurations/fido2' Path = 'Policies/AuthenticationMethodsPolicy/AuthenticationMethodConfigurations/FIDO2.json' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' Path = 'Policies/AuthenticationMethodsPolicy/AuthenticationMethodConfigurations/MicrosoftAuthenticator.json' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/authenticationMethodsPolicy/authenticationMethodConfigurations/sms' Path = 'Policies/AuthenticationMethodsPolicy/AuthenticationMethodConfigurations/SMS.json' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/authenticationMethodsPolicy/authenticationMethodConfigurations/temporaryAccessPass' Path = 'Policies/AuthenticationMethodsPolicy/AuthenticationMethodConfigurations/TemporaryAccessPass.json' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' Path = 'Policies/AuthenticationMethodsPolicy/AuthenticationMethodConfigurations/SoftwareOath.json' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/authenticationMethodsPolicy/authenticationMethodConfigurations/voice' Path = 'Policies/AuthenticationMethodsPolicy/AuthenticationMethodConfigurations/Voice.json' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/authenticationMethodsPolicy/authenticationMethodConfigurations/x509Certificate' Path = 'Policies/AuthenticationMethodsPolicy/AuthenticationMethodConfigurations/X509Certificate.json' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/adminConsentRequestPolicy' Path = 'Policies/AdminConsentRequestPolicy' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/permissionGrantPolicies' Path = 'Policies/PermissionGrantPolicies' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.PermissionGrant' ApplicationPermission = 'Policy.Read.PermissionGrant' }, @{ GraphUri = 'policies/externalIdentitiesPolicy' Path = 'Policies/ExternalIdentitiesPolicy' ApiVersion = 'beta' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/crossTenantAccessPolicy' Path = 'Policies/CrossTenantAccessPolicy' ApiVersion = 'beta' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/crossTenantAccessPolicy/default' Path = 'Policies/CrossTenantAccessPolicy/Default' ApiVersion = 'beta' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'policies/crossTenantAccessPolicy/partners' Path = 'Policies/CrossTenantAccessPolicy/Partners' ApiVersion = 'beta' Tag = @('All', 'Config', 'Policies') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'identity/customAuthenticationExtensions' Path = 'Identity/CustomAuthenticationExtensions' ApiVersion = 'beta' Tag = @('All', 'Config') DelegatedPermission = 'Application.Read.All' ApplicationPermission = 'Application.Read.All' }, # Conditional Access @{ GraphUri = 'identity/conditionalAccess/policies' Path = 'Identity/Conditional/AccessPolicies' ApiVersion = 'beta' Tag = @('All', 'Config', 'ConditionalAccess') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, @{ GraphUri = 'identity/conditionalAccess/namedLocations' Path = 'Identity/Conditional/NamedLocations' Tag = @('All', 'Config', 'ConditionalAccess') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All' }, # Identity Governance, @{ GraphUri = 'identityGovernance/entitlementManagement/accessPackages' Path = 'IdentityGovernance\EntitlementManagement\AccessPackages' ApiVersion = 'beta' Tag = @('All', 'Governance', 'EntitlementManagement') DelegatedPermission = 'EntitlementManagement.Read.All' ApplicationPermission = 'EntitlementManagement.Read.All' Children = @( @{ Command = 'Get-EEAccessPackageAssignmentPolicies' Path = 'AssignmentPolicies' Tag = @('All', 'Governance', 'EntitlementManagement') DelegatedPermission = 'EntitlementManagement.Read.All' ApplicationPermission = 'EntitlementManagement.Read.All' }, @{ Command = 'Get-EEAccessPackageAssignments' Path = 'Assignments' Tag = @('All', 'Governance', 'EntitlementManagement') DelegatedPermission = 'EntitlementManagement.Read.All' ApplicationPermission = 'EntitlementManagement.Read.All' }, @{ Command = 'Get-EEAccessPackageResourceScopes' Path = 'ResourceScopes' Tag = @('All', 'Governance', 'EntitlementManagement') DelegatedPermission = 'EntitlementManagement.Read.All' ApplicationPermission = 'EntitlementManagement.Read.All' } ) }, @{ GraphUri = 'identityGovernance/accessReviews/definitions' Path = 'IdentityGovernance/AccessReviews' ApiVersion = 'beta' Tag = @('All', 'AccessReviews', 'Governance') DelegatedPermission = 'AccessReview.Read.All' ApplicationPermission = 'AccessReview.Read.All' Children = @( @{ GraphUri = 'identityGovernance/accessReviews/definitions/{id}/instances' Path = '' Tag = @('All', 'AccessReviews', 'Governance') DelegatedPermission = 'AccessReview.Read.All' ApplicationPermission = 'AccessReview.Read.All' Children = @( @{ GraphUri = 'identityGovernance/accessReviews/definitions/{id}/instances/{id}/contactedReviewers' Path = 'Reviewers' ApiVersion = 'beta' Tag = @('All', 'AccessReviews', 'Governance') DelegatedPermission = 'AccessReview.Read.All' ApplicationPermission = 'AccessReview.Read.All' } ) } ) }, @{ GraphUri = 'identityGovernance/termsOfUse/agreements' Path = 'IdentityGovernance/TermsOfUse/Agreements' Tag = @('All', 'Config', 'Governance') DelegatedPermission = 'Agreement.Read.All' }, @{ GraphUri = 'identityGovernance/entitlementManagement/connectedOrganizations' Path = 'IdentityGovernance/EntitlementManagement/ConnectedOrganizations' ApiVersion = 'beta' Tag = @('All', 'Config') DelegatedPermission = 'EntitlementManagement.Read.All' ApplicationPermission = 'EntitlementManagement.Read.All' Children = @( @{ GraphUri = 'identityGovernance/entitlementManagement/connectedOrganizations/{id}/externalSponsors' Path = 'ExternalSponsors' ApiVersion = 'beta' Tag = @('All', 'Config', 'Governance') DelegatedPermission = 'EntitlementManagement.Read.All' ApplicationPermission = 'EntitlementManagement.Read.All' }, @{ GraphUri = 'identityGovernance/entitlementManagement/connectedOrganizations/{id}/internalSponsors' Path = 'InternalSponsors' ApiVersion = 'beta' Tag = @('All', 'Config', 'Governance') DelegatedPermission = 'EntitlementManagement.Read.All' ApplicationPermission = 'EntitlementManagement.Read.All' } ) }, @{ GraphUri = 'identityGovernance/entitlementManagement/settings' Path = 'IdentityGovernance/EntitlementManagement/Settings' ApiVersion = 'beta' Tag = @('All', 'Config', 'Governance') DelegatedPermission = 'EntitlementManagement.Read.All' ApplicationPermission = 'EntitlementManagement.Read.All' }, @{ GraphUri = 'AdministrativeUnits' Path = 'AdministrativeUnits' ApiVersion = 'beta' Tag = @('All', 'Config', 'AdministrativeUnits') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' Children = @( @{ GraphUri = 'administrativeUnits/{id}/members' Select = 'Id' Path = 'Members' ApiVersion = 'beta' Tag = @('All', 'Config', 'AdministrativeUnits') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' }, @{ GraphUri = 'administrativeUnits/{id}/scopedRoleMembers' Path = 'ScopedRoleMembers' ApiVersion = 'beta' Tag = @('All', 'Config', 'AdministrativeUnits') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' }, @{ GraphUri = 'administrativeUnits/{id}/extensions' Path = 'Extensions' ApiVersion = 'beta' Tag = @('All', 'Config', 'AdministrativeUnits') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' } ) }, # PIM @{ GraphUri = 'privilegedAccess/aadroles/resources' Path = 'PrivilegedAccess/AADRoles/Resources' ApiVersion = 'beta' Tag = @('All', 'Config', 'PIM', 'PIMAAD') DelegatedPermission = 'PrivilegedAccess.ReadWrite.AzureAD' ApplicationPermission = 'PrivilegedAccess.Read.AzureAD' Children = @( @{ GraphUri = 'privilegedAccess/aadroles/resources/{id}/roleDefinitions' Path = 'RoleDefinitions' ApiVersion = 'beta' Filter = "Type ne 'BuiltInRole'" Tag = @('All', 'Config', 'PIM', 'PIMAAD') DelegatedPermission = 'PrivilegedAccess.ReadWrite.AzureAD' ApplicationPermission = 'PrivilegedAccess.Read.AzureAD' }, @{ GraphUri = 'privilegedAccess/aadroles/resources/{id}/roleSettings' Path = 'RoleSettings' ApiVersion = 'beta' Filter = 'isDefault eq false' Tag = @('All', 'Config', 'PIM', 'PIMAAD') DelegatedPermission = 'PrivilegedAccess.ReadWrite.AzureAD' ApplicationPermission = 'PrivilegedAccess.Read.AzureAD' }, @{ GraphUri = 'privilegedAccess/aadroles/resources/{id}/roleAssignments' Path = 'RoleAssignments' ApiVersion = 'beta' Filter = 'endDateTime eq null' Tag = @('All', 'Config', 'PIM', 'PIMAAD') DelegatedPermission = 'PrivilegedAccess.ReadWrite.AzureAD' ApplicationPermission = 'PrivilegedAccess.Read.AzureAD' } ) }, @{ GraphUri = 'privilegedAccess/azureResources/resources' Path = 'PrivilegedAccess/AzureResources/Resources' ApiVersion = 'beta' IgnoreError = 'The tenant has not onboarded to PIM.' Tag = @('All', 'Config', 'PIM', 'PIMAzure') DelegatedPermission = 'PrivilegedAccess.ReadWrite.AzureResources' ApplicationPermission = 'PrivilegedAccess.Read.AzureResources' Children = @( @{ GraphUri = 'privilegedAccess/azureResources/resources/{id}/roleDefinitions' Path = 'RoleDefinitions' ApiVersion = 'beta' Filter = "Type ne 'BuiltInRole'" Tag = @('All', 'Config', 'PIM', 'PIMAzure') DelegatedPermission = 'PrivilegedAccess.ReadWrite.AzureResources' ApplicationPermission = 'PrivilegedAccess.Read.AzureResources' }, @{ GraphUri = 'privilegedAccess/azureResources/resources/{id}/roleSettings' Path = 'RoleSettings' ApiVersion = 'beta' Filter = 'isDefault eq false' Tag = @('All', 'Config', 'PIM', 'PIMAzure') DelegatedPermission = 'PrivilegedAccess.ReadWrite.AzureResources' ApplicationPermission = 'PrivilegedAccess.Read.AzureResources' }, @{ GraphUri = 'privilegedAccess/azureResources/resources/{id}/roleAssignments' Path = 'RoleAssignments' ApiVersion = 'beta' Filter = 'endDateTime eq null' Tag = @('All', 'Config', 'PIM', 'PIMAzure') DelegatedPermission = 'PrivilegedAccess.ReadWrite.AzureResources' ApplicationPermission = 'PrivilegedAccess.Read.AzureResources' } ) }, #Application Proxy @{ GraphUri = 'onPremisesPublishingProfiles/provisioning' QueryParameters = @{ expand = 'publishedResources,agents,agentGroups' } Path = 'OnPremisesPublishingProfiles/Provisioning.json' ApiVersion = 'beta' Tag = @('All', 'Config', 'AppProxy') DelegatedPermission = 'OnPremisesPublishingProfiles.ReadWrite.All' }, @{ GraphUri = 'onPremisesPublishingProfiles/provisioning/publishedResources' QueryParameters = @{ expand = 'agentGroups' } Path = 'OnPremisesPublishingProfiles/Provisioning/PublishedResources' ApiVersion = 'beta' Tag = @('All', 'Config', 'AppProxy') DelegatedPermission = 'OnPremisesPublishingProfiles.ReadWrite.All' }, @{ GraphUri = 'onPremisesPublishingProfiles/provisioning/agentGroups' QueryParameters = @{ expand = 'agents,publishedResources' } Path = 'OnPremisesPublishingProfiles/Provisioning/AgentGroups' ApiVersion = 'beta' Tag = @('All', 'Config', 'AppProxy') DelegatedPermission = 'OnPremisesPublishingProfiles.ReadWrite.All' }, @{ GraphUri = 'onPremisesPublishingProfiles/provisioning/agents' QueryParameters = @{ expand = 'agentGroups' } Path = 'OnPremisesPublishingProfiles/Provisioning/Agents' ApiVersion = 'beta' Tag = @('All', 'Config', 'AppProxy') DelegatedPermission = 'OnPremisesPublishingProfiles.ReadWrite.All' }, @{ GraphUri = 'onPremisesPublishingProfiles/applicationProxy/connectors' Path = 'OnPremisesPublishingProfiles/ApplicationProxy/Connectors' ApiVersion = 'beta' Tag = @('All', 'Config', 'AppProxy') DelegatedPermission = 'Directory.ReadWrite.All' }, @{ GraphUri = 'onPremisesPublishingProfiles/applicationProxy/connectorGroups' Path = 'OnPremisesPublishingProfiles/ApplicationProxy/ConnectorGroups' ApiVersion = 'beta' Tag = @('All', 'Config', 'AppProxy') DelegatedPermission = 'Directory.ReadWrite.All' Children = @( @{ GraphUri = 'onPremisesPublishingProfiles/applicationProxy/connectorGroups/{id}/applications' Path = 'Applications' ApiVersion = 'beta' IgnoreError = 'ApplicationsForGroup_NotFound' Tag = @('All', 'Config', 'AppProxy') DelegatedPermission = 'Directory.ReadWrite.All' }, @{ GraphUri = 'onPremisesPublishingProfiles/applicationProxy/connectorGroups/{id}/members' Path = 'Members' ApiVersion = 'beta' IgnoreError = 'ConnectorGroup_NotFound' Tag = @('All', 'Config', 'AppProxy') DelegatedPermission = 'Directory.ReadWrite.All' } ) }, # Groups # need to looks at app roles assignements # expanding app roles assignements breaks 'ne' filtering (needs eventual consistency and count) @{ GraphUri = 'groups' Filter = "groupTypes/any(c:c eq 'DynamicMembership')" Path = 'Groups' QueryParameters = @{ '$count' = 'true'; expand = 'extensions' } ApiVersion = 'beta' Tag = @('All', 'Config', 'Groups') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' Children = @( @{ GraphUri = 'groups/{id}/owners' Select = 'id, userPrincipalName, displayName' Path = 'Owners' Tag = @('All', 'Config', 'Groups') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' } ) }, @{ GraphUri = 'groups' Filter = "not(groupTypes/any(c:c eq 'DynamicMembership'))" Path = 'Groups' QueryParameters = @{ '$count' = 'true'; expand = 'extensions' } ApiVersion = 'beta' Tag = @('All', 'Groups') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' Children = @( @{ GraphUri = 'groups/{id}/owners' Select = 'id, userPrincipalName, displayName' Path = 'Owners' Tag = @('All', 'Config', 'Groups') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' }, @{ GraphUri = 'groups/{id}/members' Select = 'id, userPrincipalName, displayName' Path = 'Members' Tag = @('All', 'Groups') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' } ) }, @{ GraphUri = 'groupSettings' Path = 'GroupSettings' Tag = @('All', 'Config', 'Groups') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' }, # Applications @{ GraphUri = 'applications' Path = 'Applications' Tag = @('All', 'Applications') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' Children = @( @{ GraphUri = 'applications/{id}/extensionProperties' Path = 'ExtensionProperties' Tag = @('All', 'Applications') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' }, @{ GraphUri = 'applications/{id}/owners' Select = 'id, userPrincipalName, displayName' Path = 'Owners' Tag = @('All', 'Applications') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' }, @{ GraphUri = 'applications/{id}/tokenIssuancePolicies' Path = 'TokenIssuancePolicies' Tag = @('All', 'Applications') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All','Application.ReadWrite.All' }, @{ GraphUri = 'applications/{id}/tokenLifetimePolicies' Path = 'TokenLifetimePolicies' Tag = @('All', 'Applications') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All','Application.ReadWrite.All' } ) }, # Service Principals @{ GraphUri = 'servicePrincipals' Path = 'ServicePrincipals' Tag = @('All', 'ServicePrincipals') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' Children = @( @{ GraphUri = 'servicePrincipals/{id}/appRoleAssignments' Path = 'AppRoleAssignments' Tag = @('All', 'ServicePrincipals') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' }, @{ GraphUri = 'servicePrincipals/{id}/appRoleAssignedTo' Path = 'AppRoleAssignedTo' Tag = @('All', 'ServicePrincipals') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' }, @{ GraphUri = 'servicePrincipals/{id}/oauth2PermissionGrants' Path = 'Oauth2PermissionGrants' Tag = @('All', 'ServicePrincipals') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' }, @{ GraphUri = 'servicePrincipals/{id}/delegatedPermissionClassifications' Path = 'DelegatedPermissionClassifications' Tag = @('All', 'ServicePrincipals') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' }, @{ GraphUri = 'servicePrincipals/{id}/owners' Select = 'id, userPrincipalName, displayName' Path = 'Owners' Tag = @('All', 'ServicePrincipals') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' }, @{ GraphUri = 'servicePrincipals/{id}/claimsMappingPolicies' Path = 'claimsMappingPolicies' Tag = @('All', 'ServicePrincipals') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All','Application.ReadWrite.All' }, @{ GraphUri = 'servicePrincipals/{id}/homeRealmDiscoveryPolicies' Path = 'homeRealmDiscoveryPolicies' Tag = @('All', 'ServicePrincipals') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All','Application.ReadWrite.All' }, @{ GraphUri = 'servicePrincipals/{id}/tokenIssuancePolicies' Path = 'tokenIssuancePolicies' Tag = @('All', 'ServicePrincipals') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All','Application.ReadWrite.All' }, @{ GraphUri = 'servicePrincipals/{id}/tokenLifetimePolicies' Path = 'tokenLifetimePolicies' Tag = @('All', 'ServicePrincipals') DelegatedPermission = 'Policy.Read.All' ApplicationPermission = 'Policy.Read.All','Application.ReadWrite.All' } ) }, # Users # Todo look at app roles assignments @{ GraphUri = 'users' Path = 'Users' Filter = $null QueryParameters = @{ '$count' = 'true'; expand = "extensions" } ApiVersion = 'beta' Tag = @('All', 'Users') DelegatedPermission = 'Directory.Read.All' ApplicationPermission = 'Directory.Read.All' } ) } #endregion #region Get-EERequiredScopes.ps1 <# .Synopsis Gets the required scopes for schema .Description Gets the require scopes for schema .Example Get-EERequiredScopes #> function Get-EERequiredScopes { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateSet('Delegated','Application')] [string]$PermissionType, [Parameter(Mandatory = $false)] [object]$ExportSchema ) if (!$ExportSchema) { $ExportSchema = Get-EEDefaultSchema } $scopeProperty = "DelegatedPermission" if ($PermissionType -eq "Application") { $scopeProperty = "ApplicationPermission" } $scopes = @() foreach($entry in $ExportSchema) { $entryScopes = Get-ObjectProperty $entry $scopeProperty $command = Get-ObjectProperty $entry 'Command' $graphUri = Get-ObjectProperty $entry 'GraphUri' $entryType = "graphuri" $tocall = $graphUri if ($command) { $entryType = "command" $tocall = $command } if (!$entryScopes) { write-warning "call to $entryType '$tocall' doesn't provide $PermissionType permissions" } foreach ($entryScope in $entryScopes) { if ($entryScope -notin $scopes) { $scopes += $entryScope } } if ($entry.ContainsKey('Children')) { $childScopes = Get-EERequiredScopes -PermissionType $PermissionType -ExportSchema $entry.Children foreach ($entryScope in $childScopes) { if ($entryScope -notin $scopes) { $scopes += $entryScope } } } } $scopes | sort-object } #endregion #region Get-EEAccessPackageAssignmentPolicies.ps1 <# .Synopsis Gets the list of accessPackageAssignmentPolicies .Description GET /identityGovernance/entitlementManagement/accessPackageAssignmentPolicies https://docs.microsoft.com/en-us/graph/api/accesspackageassignmentpolicy-list?view=graph-rest-beta&tabs=http .Example EEAccessPackagesAssignmentPolicies #> Function Get-EEAccessPackageAssignmentPolicies { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string[]]$Parents ) Invoke-Graph 'identityGovernance/entitlementManagement/accessPackageAssignmentPolicies' -Filter "(accessPackage/id eq '$($Parents[0])')" -ApiVersion 'beta' } #endregion #region Get-EEAccessPackageAssignments.ps1 <# .Synopsis Gets the list of accessPackageAssignments .Description GET /identityGovernance/entitlementManagement/accessPackageAssignments?$filter=accessPackage/id eq https://docs.microsoft.com/en-us/graph/api/accesspackageassignment-list?view=graph-rest-beta&tabs=http .Example Get-EEAccessPackagesAssignments #> Function Get-EEAccessPackageAssignments { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string[]]$Parents ) Invoke-Graph 'identityGovernance/entitlementManagement/accessPackageAssignments' -Filter "(accessPackage/id eq '$($Parents[0])')" -ApiVersion 'beta' } #endregion #region Get-EEAccessPackageResourceScopes.ps1 <# .Synopsis Gets the list of businessflowtemplatesRetrieve a list of accessPackage objects. .Description GET /identityGovernance/entitlementManagement/accessPackages/{id}?$expand=accessPackageResourceRoleScopes($expand=accessPackageResourceRole,accessPackageResourceScope) https://docs.microsoft.com/en-us/graph/api/accesspackage-list-accesspackageresourcerolescopes?view=graph-rest-beta&tabs=http .Example Get-EEAccessPackageResourceScopes #> Function Get-EEAccessPackageResourceScopes { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string[]]$Parents ) Invoke-Graph "identityGovernance/entitlementManagement/accessPackages/$($Parents[0])" -QueryParameters @{expand='accessPackageResourceRoleScopes(expand=accessPackageResourceRole,accessPackageResourceScope)'} -ApiVersion 'beta' } #endregion #endregion <# .DISCLAIMER THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. Copyright (c) Microsoft Corporation. All rights reserved. #> ## Set Strict Mode for Module. https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/set-strictmode Set-StrictMode -Version 3.0 ## Initialize Module Configuration ## Initialize Module Variables Export-ModuleMember -Function @('Connect-EntraExporter','Export-Entra') -Cmdlet @() -Variable @() -Alias @() # SIG # Begin signature block # MIIoKgYJKoZIhvcNAQcCoIIoGzCCKBcCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCi5ZgzkIbFOHcC # yF/rdQ7v6qohyM80OttGpVIwhH3n66CCDXYwggX0MIID3KADAgECAhMzAAADTrU8 # esGEb+srAAAAAANOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI5WhcNMjQwMzE0MTg0MzI5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDdCKiNI6IBFWuvJUmf6WdOJqZmIwYs5G7AJD5UbcL6tsC+EBPDbr36pFGo1bsU # p53nRyFYnncoMg8FK0d8jLlw0lgexDDr7gicf2zOBFWqfv/nSLwzJFNP5W03DF/1 # 1oZ12rSFqGlm+O46cRjTDFBpMRCZZGddZlRBjivby0eI1VgTD1TvAdfBYQe82fhm # WQkYR/lWmAK+vW/1+bO7jHaxXTNCxLIBW07F8PBjUcwFxxyfbe2mHB4h1L4U0Ofa # +HX/aREQ7SqYZz59sXM2ySOfvYyIjnqSO80NGBaz5DvzIG88J0+BNhOu2jl6Dfcq # jYQs1H/PMSQIK6E7lXDXSpXzAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUnMc7Zn/ukKBsBiWkwdNfsN5pdwAw # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzUwMDUxNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAD21v9pHoLdBSNlFAjmk # mx4XxOZAPsVxxXbDyQv1+kGDe9XpgBnT1lXnx7JDpFMKBwAyIwdInmvhK9pGBa31 # TyeL3p7R2s0L8SABPPRJHAEk4NHpBXxHjm4TKjezAbSqqbgsy10Y7KApy+9UrKa2 # kGmsuASsk95PVm5vem7OmTs42vm0BJUU+JPQLg8Y/sdj3TtSfLYYZAaJwTAIgi7d # hzn5hatLo7Dhz+4T+MrFd+6LUa2U3zr97QwzDthx+RP9/RZnur4inzSQsG5DCVIM # pA1l2NWEA3KAca0tI2l6hQNYsaKL1kefdfHCrPxEry8onJjyGGv9YKoLv6AOO7Oh # JEmbQlz/xksYG2N/JSOJ+QqYpGTEuYFYVWain7He6jgb41JbpOGKDdE/b+V2q/gX # UgFe2gdwTpCDsvh8SMRoq1/BNXcr7iTAU38Vgr83iVtPYmFhZOVM0ULp/kKTVoir # IpP2KCxT4OekOctt8grYnhJ16QMjmMv5o53hjNFXOxigkQWYzUO+6w50g0FAeFa8 # 5ugCCB6lXEk21FFB1FdIHpjSQf+LP/W2OV/HfhC3uTPgKbRtXo83TZYEudooyZ/A # Vu08sibZ3MkGOJORLERNwKm2G7oqdOv4Qj8Z0JrGgMzj46NFKAxkLSpE5oHQYP1H # tPx1lPfD7iNSbJsP6LiUHXH1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 # IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg # Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03 # a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr # rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg # OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy # 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9 # sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh # dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k # A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB # w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn # Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90 # lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w # ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o # ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa # BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG # AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV # HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG # AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl # AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb # C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l # hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6 # I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0 # wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560 # STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam # ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa # J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah # XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA # 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt # Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr # /Xmfwb1tbWrJUnMTDXpQzTGCGgowghoGAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIFnMTy4PL0n8+xC8FvJBKkXK # 9zD2WiMa6JmyxeiWfXaBMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEA1KiMx3dPDmva1hxRIa4iajcr30NTyieeKPoqI5PpXwrKDWM0Lh6klkUi # KlaaIcn2vkPT2C6IlRr1M9Tezwt7GPynTQJ1vXREoiYbNmqj19ZvKT+llf9uofNP # Y0Rdm5m2nIt2mKy984zJTQByaBxrcPmtP92seWw210fq4dT4LDJhN55SHpUSWHbZ # j7cMmNXxYH8dMruyLTlUHKOuA/HX/dtruekz8PIGK8I+Dib46B9gU4OcbNp/dTXZ # 7avOUwwANYVXprYOsey0D5LfeNgIXGfJZ5M31BDtHl5fvqc+9sQfVXEGOyoHWtxz # nSFZqYt57fQ78XvsBZkqfWPA4U3OLaGCF5QwgheQBgorBgEEAYI3AwMBMYIXgDCC # F3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq # hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCBFYzoucrYa80KfHbkpez1v+9XIFu1RVf7YMjYQIOzo1AIGZNTJKx54 # GBMyMDIzMDgyMzA5MTYxNS42NDZaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l # cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046QTQwMC0w # NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg # ghHqMIIHIDCCBQigAwIBAgITMwAAAdYnaf9yLVbIrgABAAAB1jANBgkqhkiG9w0B # AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzA1MjUxOTEy # MzRaFw0yNDAyMDExOTEyMzRaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z # MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046QTQwMC0wNUUwLUQ5NDcxJTAjBgNV # BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQDPLM2Om8r5u3fcbDEOXydJtbkW5U34KFaftC+8QyNq # plMIzSTC1ToE0zcweQCvPIfpYtyPB3jt6fPRprvhwCksUw9p0OfmZzWPDvkt40BU # Stu813QlrloRdplLz2xpk29jIOZ4+rBbKaZkBPZ4R4LXQhkkHne0Y/Yh85ZqMMRa # MWkBM6nUwV5aDiwXqdE9Jyl0i1aWYbCqzwBRdN7CTlAJxrJ47ov3uf/lFg9hnVQc # qQYgRrRFpDNFMOP0gwz5Nj6a24GZncFEGRmKwImL+5KWPnVgvadJSRp6ZqrYV3Fm # bBmZtsF0hSlVjLQO8nxelGV7TvqIISIsv2bQMgUBVEz8wHFyU3863gHj8BCbEpJz # m75fLJsL3P66lJUNRN7CRsfNEbHdX/d6jopVOFwF7ommTQjpU37A/7YR0wJDTt6Z # sXU+j/wYlo9b22t1qUthqjRs32oGf2TRTCoQWLhJe3cAIYRlla/gEKlbuDDsG392 # 6y4EMHFxTjsjrcZEbDWwjB3wrp11Dyg1QKcDyLUs2anBolvQwJTN0mMOuXO8tBz2 # 0ng/+Xw+4w+W9PMkvW1faYi435VjKRZsHfxIPjIzZ0wf4FibmVPJHZ+aTxGsVJPx # ydChvvGCf4fe8XfYY9P5lbn9ScKc4adTd44GCrBlJ/JOsoA4OvNHY6W+XcKVcIIG # WwIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFGGaVDY7TQBiMCKg2+j/zRTcYsZOMB8G # A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG # Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy # MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w # XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy # dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG # A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD # AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQDUv+RjNidwJxSbMk1IvS8LfxNM8VaVhpxR # 1SkW+FRY6AKkn2s3On29nGEVlatblIv1qVTKkrUxLYMZ0z+RA6mmfXue2Y7/YBbz # M5kUeUgU2y1Mmbin6xadT9DzECeE7E4+3k2DmZxuV+GLFYQsqkDbe8oy7+3BSiU2 # 9qyZAYT9vRDALPUC5ZwyoPkNfKbqjl3VgFTqIubEQr56M0YdMWlqCqq0yVln9mPb # hHHzXHOjaQsurohHCf7VT8ct79po34Fd8XcsqmyhdKBy1jdyknrik+F3vEl/90qa # on5N8KTZoGtOFlaJFPnZ2DqQtb2WWkfuAoGWrGSA43Myl7+PYbUsri/NrMvAd9Z+ # J9FlqsMwXQFxAB7ujJi4hP8BH8j6qkmy4uulU5SSQa6XkElcaKQYSpJcSjkjyTDI # Opf6LZBTaFx6eeoqDZ0lURhiRqO+1yo8uXO89e6kgBeC8t1WN5ITqXnjocYgDvyF # pptsUDgnRUiI1M/Ql/O299VktMkIL72i6Qd4BBsrj3Z+iLEnKP9epUwosP1m3N2v # 9yhXQ1HiusJl63IfXIyfBJaWvQDgU3Jk4eIZSr/2KIj4ptXt496CRiHTi011kcwD # pdjQLAQiCvoj1puyhfwVf2G5ZwBptIXivNRba34KkD5oqmEoF1yRFQ84iDsf/giy # n/XIT7YY/zCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI # hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy # MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC # AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg # M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF # dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6 # GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp # Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu # yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E # XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0 # lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q # GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ # +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA # PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw # EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG # NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV # MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj # cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK # BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG # 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x # M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC # VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449 # xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM # nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS # PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d # Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn # GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs # QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL # jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL # 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNN # MIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn # MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOkE0MDAtMDVFMC1EOTQ3MSUwIwYDVQQD # ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQD5 # r3DVRpAGQo9sTLUHeBC87NpK+qCBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6I/BmjAiGA8yMDIzMDgyMjIzMjIw # MloYDzIwMjMwODIzMjMyMjAyWjB0MDoGCisGAQQBhFkKBAExLDAqMAoCBQDoj8Ga # AgEAMAcCAQACAiCqMAcCAQACAhKhMAoCBQDokRMaAgEAMDYGCisGAQQBhFkKBAIx # KDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZI # hvcNAQELBQADggEBAF+P+vYI5nAVMKLZzuKDTypkyGszcERltVOmBhfuSHAHWdJW # ny4lnCYauOWDJ0eF9wYyeIixbFmF7FSQipTB5IlktuOSFKh30qSWPT3vP/KNc2ul # OzWwoGrEAt5ZiGdm9/6Cotg6rl9g8WSJkaOhJ5ZIIoyj+VzI8B4Fd6OvoQWQC6S2 # eQHLPTvLZX3OU3In5T11pZUkBL63VP1OkirAZZmH7wog3g1/0nU4ThMh/GkPU9E5 # 2YPz8nfe0+ttMemFoBbBa/OynYKgdoiE2Xfay6PGWhFPk25p+nQ+qV/a5v0FzPGT # 3fZ8LWxUSXRqH0YqIVdD0bUJJ7yETFl9iSj+7AQxggQNMIIECQIBATCBkzB8MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy # b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAdYnaf9yLVbIrgABAAAB1jAN # BglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8G # CSqGSIb3DQEJBDEiBCAXP5ysINYoCtcS9+iN/YDBUzTF/j+GLeER6tqecIo9yzCB # +gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EINbLTQ1XeNM+EBinOEJMjZd0jMND # ur+AK+O8P12j5ST8MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw # MTACEzMAAAHWJ2n/ci1WyK4AAQAAAdYwIgQgOLLykD7vmwAbIXPhNyhZkvq4uiZ6 # 24puG6TAZkSHaoswDQYJKoZIhvcNAQELBQAEggIAk1dwCEjelQ6+XDFK4zT/ulkA # OQsty8Opa7vo3r7j58anE6atuMKxqyeoK3yMTECqTV8wL+LDvgqwULhFTV3S5gSJ # oHiNzpjxE93tP5sjRD7IINEBQQuMc0i3uX8lG9VF7cYoiE3ITBIzQJofouh1AvWn # BCPtXbE5rhLIa9pgAw/1neGtT69iG8bDtE1RCg+8/raN4g7Zdb61Y+gcU5vnkRqp # uMPmh6JpQuqzpyn9vKNX5UZNsdorFNFDEWn/i1Mci8/TKYiHIDqHvGU4Idhqcg6n # WZtPnPM3o9//0Vsp16hMcA6+EYpqN1ta3qbtJjkoR+4RAPEBgyrlYWFu3Yuojqnn # MO7bdraX1IHdqq+G7qxa5Vs5h+hi5gWU5qQ6MqZZ6bU4c6cgdMi121EXAeqZqpe3 # bxf8CyeUQRNBUkNY6ZKHX6xLNghppoAOUwXC9gk69XWRszDWR+fCikcXzvBtmuIw # AeV01UB4I7srVJg3iSORCCDCQ2hp3ETDhAtCOUumd6HuoRC1+mcmAQAqdW/vcTtv # v6yNwvFpedBvQasRYWjWdR8PvvBht/8DgpusaLN40+fOGNINgRyhoAJyspIZqrEb # Y3Lg8Fg7S9tZ7n4DGBLaWVg4qsXl/E5Zi6LHo27IecbRt1/4CZubdXMjt0u0CYzI # bRxj7vxDsEp+MjFena8= # SIG # End signature block |