netCore/ExchangeOnlineManagement.psm1
# Import the REST module so that the EXO* cmdlets are present before Connect-ExchangeOnline in the powershell instance. $RestModule = "Microsoft.Exchange.Management.RestApiClient.dll" $RestModulePath = [System.IO.Path]::Combine($PSScriptRoot, $RestModule) Import-Module $RestModulePath $ExoPowershellModule = "Microsoft.Exchange.Management.ExoPowershellGalleryModule.dll" $ExoPowershellModulePath = [System.IO.Path]::Combine($PSScriptRoot, $ExoPowershellModule) Import-Module $ExoPowershellModulePath ############# Helper Functions Begin ############# <# Details to be printed on the console when the Connect-ExchangeOnline function is run #> function Print-Details { if ($UseRPSSession) { Write-Host -ForegroundColor Yellow "" Write-Host -ForegroundColor Yellow "----------------------------------------------------------------------------" Write-Host -ForegroundColor Yellow "The module allows access to all existing remote PowerShell (V1) cmdlets in addition to the 9 new, faster, and more reliable cmdlets." Write-Host -ForegroundColor Yellow "" Write-Host -ForegroundColor Yellow "|--------------------------------------------------------------------------|" Write-Host -ForegroundColor Yellow "| Old Cmdlets | New/Reliable/Faster Cmdlets |" Write-Host -ForegroundColor Yellow "|--------------------------------------------------------------------------|" Write-Host -ForegroundColor Yellow "| Get-CASMailbox | Get-EXOCASMailbox |" Write-Host -ForegroundColor Yellow "| Get-Mailbox | Get-EXOMailbox |" Write-Host -ForegroundColor Yellow "| Get-MailboxFolderPermission | Get-EXOMailboxFolderPermission |" Write-Host -ForegroundColor Yellow "| Get-MailboxFolderStatistics | Get-EXOMailboxFolderStatistics |" Write-Host -ForegroundColor Yellow "| Get-MailboxPermission | Get-EXOMailboxPermission |" Write-Host -ForegroundColor Yellow "| Get-MailboxStatistics | Get-EXOMailboxStatistics |" Write-Host -ForegroundColor Yellow "| Get-MobileDeviceStatistics | Get-EXOMobileDeviceStatistics |" Write-Host -ForegroundColor Yellow "| Get-Recipient | Get-EXORecipient |" Write-Host -ForegroundColor Yellow "| Get-RecipientPermission | Get-EXORecipientPermission |" Write-Host -ForegroundColor Yellow "|--------------------------------------------------------------------------|" Write-Host -ForegroundColor Yellow "" Write-Host -ForegroundColor Yellow "To get additional information, run: Get-Help Connect-ExchangeOnline or check https://aka.ms/exops-docs" Write-Host -ForegroundColor Yellow "" Write-Host -ForegroundColor Yellow "Send your product improvement suggestions and feedback to exocmdletpreview@service.microsoft.com. For issues related to the module, contact Microsoft support. Don't use the feedback alias for problems or support issues." Write-Host -ForegroundColor Yellow "----------------------------------------------------------------------------" Write-Host -ForegroundColor Yellow "" } else { Write-Host -ForegroundColor Yellow "" Write-Host -ForegroundColor Yellow "----------------------------------------------------------------------------------------" Write-Host -ForegroundColor Yellow "This version of EXO PowerShell V2 module contains new REST API backed cmdlets which doesn't require Windows Remote PowerShell for client-server communication." Write-Host -ForegroundColor Yellow "" Write-Host -ForegroundColor Yellow "You can now run those cmdlets after turning off WinRM Basic Auth in your client machine and thus making it more secure. Read more here - https://aka.ms/exov2-securemode" Write-Host -ForegroundColor Yellow "" Write-Host -ForegroundColor Yellow "You can share your feedback and suggestions to exocmdletpreview@service.microsoft.com" Write-Host -ForegroundColor Yellow "----------------------------------------------------------------------------------------" Write-Host -ForegroundColor Yellow "" } } <# .Synopsis Validates a given Uri #> function Test-Uri { [CmdletBinding()] [OutputType([bool])] Param ( # Uri to be validated [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)] [string] $UriString ) [Uri]$uri = $UriString -as [Uri] $uri.AbsoluteUri -ne $null -and $uri.Scheme -eq 'https' } <# .Synopsis Is Cloud Shell Environment #> function global:IsCloudShellEnvironment() { return [Microsoft.Exchange.Management.AdminApiProvider.Utility]::IsCloudShellEnvironment(); } <# .Synopsis Override Get-PSImplicitRemotingSession function for reconnection #> function global:UpdateImplicitRemotingHandler() { $modules = Get-Module tmp_* foreach ($module in $modules) { [bool]$moduleProcessed = $false [string] $moduleUrl = $module.Description [int] $queryStringIndex = $moduleUrl.IndexOf("?") if ($queryStringIndex -gt 0) { $moduleUrl = $moduleUrl.SubString(0,$queryStringIndex) } if ($moduleUrl.EndsWith("/PowerShell-LiveId", [StringComparison]::OrdinalIgnoreCase) -or $moduleUrl.EndsWith("/PowerShell", [StringComparison]::OrdinalIgnoreCase)) { & $module { ${function:Get-PSImplicitRemotingSession} = ` { param( [Parameter(Mandatory = $true, Position = 0)] [string] $commandName ) $shouldRemoveCurrentSession = $false; # Clear any left over PS tmp modules if (($script:PSSession -ne $null) -and ($script:PSSession.PreviousModuleName -ne $null) -and ($script:PSSession.PreviousModuleName -ne $script:MyModule.Name)) { Remove-Module -Name $script:PSSession.PreviousModuleName -ErrorAction SilentlyContinue $script:PSSession.PreviousModuleName = $null } if (($script:PSSession -eq $null) -or ($script:PSSession.Runspace.RunspaceStateInfo.State -ne 'Opened')) { Set-PSImplicitRemotingSession ` (& $script:GetPSSession ` -InstanceId $script:PSSession.InstanceId.Guid ` -ErrorAction SilentlyContinue ) } if ($script:PSSession -ne $null) { if ($script:PSSession.Runspace.RunspaceStateInfo.State -eq 'Disconnected') { # If we are handed a disconnected session, try re-connecting it before creating a new session. Set-PSImplicitRemotingSession ` (& $script:ConnectPSSession ` -Session $script:PSSession ` -ErrorAction SilentlyContinue) } else { # Import the module once more to ensure that Test-ActiveToken is present Import-Module $global:_EXO_ModulePath -Cmdlet Test-ActiveToken; # If there is no active token run the new session flow $hasActiveToken = Test-ActiveToken -TokenExpiryTime $script:PSSession.TokenExpiryTime $sessionIsOpened = $script:PSSession.Runspace.RunspaceStateInfo.State -eq 'Opened' if (($hasActiveToken -eq $false) -or ($sessionIsOpened -ne $true)) { #If there is no active user token or opened session then ensure that we remove the old session $shouldRemoveCurrentSession = $true; } } } if (($script:PSSession -eq $null) -or ($script:PSSession.Runspace.RunspaceStateInfo.State -ne 'Opened') -or ($shouldRemoveCurrentSession -eq $true)) { # Import the module once more to ensure that New-ExoPSSession is present Import-Module $global:_EXO_ModulePath -Cmdlet New-ExoPSSession; Write-PSImplicitRemotingMessage ('Creating a new Remote PowerShell session using Modern Authentication for implicit remoting of "{0}" command ...' -f $commandName) $session = New-ExoPSSession -PreviousSession $script:PSSession if ($session -ne $null) { if ($shouldRemoveCurrentSession -eq $true) { Remove-PSSession $script:PSSession } # Import the latest session to ensure that the next cmdlet call would occur on the new PSSession instance. $PSSessionModuleInfo = Import-PSSession $session -AllowClobber -DisableNameChecking -CommandName $script:MyModule.CommandName -FormatTypeName $script:MyModule.FormatTypeName # Add the name of the module to clean up in case of removing the broken session $session | Add-Member -NotePropertyName "CurrentModuleName" -NotePropertyValue $PSSessionModuleInfo.Name $CurrentModule = Import-Module $PSSessionModuleInfo.Path -Global -DisableNameChecking -Prefix $script:MyModule.ModulePrefix -PassThru $CurrentModule | Add-Member -NotePropertyName "ModulePrefix" -NotePropertyValue $script:MyModule.ModulePrefix $CurrentModule | Add-Member -NotePropertyName "CommandName" -NotePropertyValue $script:MyModule.CommandName $CurrentModule | Add-Member -NotePropertyName "FormatTypeName" -NotePropertyValue $script:MyModule.FormatTypeName $session | Add-Member -NotePropertyName "PreviousModuleName" -NotePropertyValue $script:MyModule.Name UpdateImplicitRemotingHandler $script:PSSession = $session } } if (($script:PSSession -eq $null) -or ($script:PSSession.Runspace.RunspaceStateInfo.State -ne 'Opened')) { throw 'No session has been associated with this implicit remoting module' } return [Management.Automation.Runspaces.PSSession]$script:PSSession }} } } } <# .SYNOPSIS Extract organization name from UserPrincipalName #> function Get-OrgNameFromUPN { param([string] $UPN) $fields = $UPN -split '@' return $fields[-1] } <# .SYNOPSIS Get the command from the given module #> function global:Get-WrappedCommand { param( [string] $CommandName, [string] $ModuleName, [string] $CommandType) $cmd = (Get-Module $moduleName).ExportedFunctions[$CommandName] return $cmd } ############# Helper Functions End ############# ###### Begin Main ###### $EOPConnectionInProgress = $false function Connect-ExchangeOnline { [CmdletBinding()] param( # Connection Uri for the Remote PowerShell endpoint [string] $ConnectionUri = '', # Azure AD Authorization endpoint Uri that can issue the OAuth2 access tokens [string] $AzureADAuthorizationEndpointUri = '', # Exchange Environment name [Microsoft.Exchange.Management.RestApiClient.ExchangeEnvironment] $ExchangeEnvironmentName = 'O365Default', # PowerShell session options to be used when opening the Remote PowerShell session [System.Management.Automation.Remoting.PSSessionOption] $PSSessionOption = $null, # Switch to bypass use of mailbox anchoring hint. [switch] $BypassMailboxAnchoring = $false, # Delegated Organization Name [string] $DelegatedOrganization = '', # Prefix [string] $Prefix = '', # Show Banner of Exchange cmdlets Mapping and recent updates [switch] $ShowBanner = $true, #Cmdlets to Import for rps cmdlets , by default it would bring all [string[]] $CommandName = @("*"), #The way the output objects would be printed on the console [string[]] $FormatTypeName = @("*"), # Use Remote PowerShell Session based connection [switch] $UseRPSSession = $false ) DynamicParam { if (($isCloudShell = IsCloudShellEnvironment) -eq $false) { $attributes = New-Object System.Management.Automation.ParameterAttribute $attributes.Mandatory = $false $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $attributeCollection.Add($attributes) # User Principal Name or email address of the user $UserPrincipalName = New-Object System.Management.Automation.RuntimeDefinedParameter('UserPrincipalName', [string], $attributeCollection) $UserPrincipalName.Value = '' # User Credential to Logon $Credential = New-Object System.Management.Automation.RuntimeDefinedParameter('Credential', [System.Management.Automation.PSCredential], $attributeCollection) $Credential.Value = $null # Certificate $Certificate = New-Object System.Management.Automation.RuntimeDefinedParameter('Certificate', [System.Security.Cryptography.X509Certificates.X509Certificate2], $attributeCollection) $Certificate.Value = $null # Certificate Path $CertificateFilePath = New-Object System.Management.Automation.RuntimeDefinedParameter('CertificateFilePath', [string], $attributeCollection) $CertificateFilePath.Value = '' # Certificate Password $CertificatePassword = New-Object System.Management.Automation.RuntimeDefinedParameter('CertificatePassword', [System.Security.SecureString], $attributeCollection) $CertificatePassword.Value = $null # Certificate Thumbprint $CertificateThumbprint = New-Object System.Management.Automation.RuntimeDefinedParameter('CertificateThumbprint', [string], $attributeCollection) $CertificateThumbprint.Value = '' # Application Id $AppId = New-Object System.Management.Automation.RuntimeDefinedParameter('AppId', [string], $attributeCollection) $AppId.Value = '' # Organization $Organization = New-Object System.Management.Automation.RuntimeDefinedParameter('Organization', [string], $attributeCollection) $Organization.Value = '' # Switch to collect telemetry on command execution. $EnableErrorReporting = New-Object System.Management.Automation.RuntimeDefinedParameter('EnableErrorReporting', [switch], $attributeCollection) $EnableErrorReporting.Value = $false # Where to store EXO command telemetry data. By default telemetry is stored in the directory "%TEMP%/EXOTelemetry" in the file : EXOCmdletTelemetry-yyyymmdd-hhmmss.csv. $LogDirectoryPath = New-Object System.Management.Automation.RuntimeDefinedParameter('LogDirectoryPath', [string], $attributeCollection) $LogDirectoryPath.Value = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "EXOCmdletTelemetry") # Create a new attribute and valiate set against the LogLevel $LogLevelAttribute = New-Object System.Management.Automation.ParameterAttribute $LogLevelAttribute.Mandatory = $false $LogLevelAttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $LogLevelAttributeCollection.Add($LogLevelAttribute) $LogLevelList = @([Microsoft.Online.CSE.RestApiPowerShellModule.Instrumentation.LogLevel]::Default, [Microsoft.Online.CSE.RestApiPowerShellModule.Instrumentation.LogLevel]::All) $ValidateSet = New-Object System.Management.Automation.ValidateSetAttribute($LogLevelList) $LogLevel = New-Object System.Management.Automation.RuntimeDefinedParameter('LogLevel', [Microsoft.Online.CSE.RestApiPowerShellModule.Instrumentation.LogLevel], $LogLevelAttributeCollection) $LogLevel.Attributes.Add($ValidateSet) # EXO params start # Switch to track perfomance $TrackPerformance = New-Object System.Management.Automation.RuntimeDefinedParameter('TrackPerformance', [bool], $attributeCollection) $TrackPerformance.Value = $false # Flag to enable or disable showing the number of objects written $ShowProgress = New-Object System.Management.Automation.RuntimeDefinedParameter('ShowProgress', [bool], $attributeCollection) $ShowProgress.Value = $false # Switch to enable/disable Multi-threading in the EXO cmdlets $UseMultithreading = New-Object System.Management.Automation.RuntimeDefinedParameter('UseMultithreading', [bool], $attributeCollection) $UseMultithreading.Value = $true # Pagesize Param $PageSize = New-Object System.Management.Automation.RuntimeDefinedParameter('PageSize', [uint32], $attributeCollection) $PageSize.Value = 1000 # Switch to MSI auth $Device = New-Object System.Management.Automation.RuntimeDefinedParameter('Device', [switch], $attributeCollection) $Device.Value = $false # Switch to CmdInline parameters $InlineCredential = New-Object System.Management.Automation.RuntimeDefinedParameter('InlineCredential', [switch], $attributeCollection) $InlineCredential.Value = $false # EXO params end $paramDictionary = New-object System.Management.Automation.RuntimeDefinedParameterDictionary $paramDictionary.Add('UserPrincipalName', $UserPrincipalName) $paramDictionary.Add('Credential', $Credential) $paramDictionary.Add('Certificate', $Certificate) $paramDictionary.Add('CertificateFilePath', $CertificateFilePath) $paramDictionary.Add('CertificatePassword', $CertificatePassword) $paramDictionary.Add('AppId', $AppId) $paramDictionary.Add('Organization', $Organization) $paramDictionary.Add('EnableErrorReporting', $EnableErrorReporting) $paramDictionary.Add('LogDirectoryPath', $LogDirectoryPath) $paramDictionary.Add('LogLevel', $LogLevel) $paramDictionary.Add('TrackPerformance', $TrackPerformance) $paramDictionary.Add('ShowProgress', $ShowProgress) $paramDictionary.Add('UseMultithreading', $UseMultithreading) $paramDictionary.Add('PageSize', $PageSize) if($PSEdition -eq 'Core') { $paramDictionary.Add('Device', $Device) $paramDictionary.Add('InlineCredential', $InlineCredential); # We do not want to expose certificate thumprint in Linux as it is not feasible there. if($IsWindows) { $paramDictionary.Add('CertificateThumbprint', $CertificateThumbprint); } } else { $paramDictionary.Add('CertificateThumbprint', $CertificateThumbprint); } return $paramDictionary } else { $attributes = New-Object System.Management.Automation.ParameterAttribute $attributes.Mandatory = $false $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $attributeCollection.Add($attributes) # Switch to MSI auth $Device = New-Object System.Management.Automation.RuntimeDefinedParameter('Device', [switch], $attributeCollection) $Device.Value = $false # Switch to collect telemetry on command execution. $EnableErrorReporting = New-Object System.Management.Automation.RuntimeDefinedParameter('EnableErrorReporting', [switch], $attributeCollection) $EnableErrorReporting.Value = $false # Where to store EXO command telemetry data. By default telemetry is stored in the directory "%TEMP%/EXOTelemetry" in the file : EXOCmdletTelemetry-yyyymmdd-hhmmss.csv. $LogDirectoryPath = New-Object System.Management.Automation.RuntimeDefinedParameter('LogDirectoryPath', [string], $attributeCollection) $LogDirectoryPath.Value = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "EXOCmdletTelemetry") # Create a new attribute and valiate set against the LogLevel $LogLevelAttribute = New-Object System.Management.Automation.ParameterAttribute $LogLevelAttribute.Mandatory = $false $LogLevelAttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $LogLevelAttributeCollection.Add($LogLevelAttribute) $LogLevelList = @([Microsoft.Online.CSE.RestApiPowerShellModule.Instrumentation.LogLevel]::Default, [Microsoft.Online.CSE.RestApiPowerShellModule.Instrumentation.LogLevel]::All) $ValidateSet = New-Object System.Management.Automation.ValidateSetAttribute($LogLevelList) $LogLevel = New-Object System.Management.Automation.RuntimeDefinedParameter('LogLevel', [Microsoft.Online.CSE.RestApiPowerShellModule.Instrumentation.LogLevel], $LogLevelAttributeCollection) $LogLevel.Attributes.Add($ValidateSet) # Switch to CmdInline parameters $InlineCredential = New-Object System.Management.Automation.RuntimeDefinedParameter('InlineCredential', [switch], $attributeCollection) $InlineCredential.Value = $false # User Credential to Logon $Credential = New-Object System.Management.Automation.RuntimeDefinedParameter('Credential', [System.Management.Automation.PSCredential], $attributeCollection) $Credential.Value = $null $paramDictionary = New-object System.Management.Automation.RuntimeDefinedParameterDictionary $paramDictionary.Add('Device', $Device) $paramDictionary.Add('EnableErrorReporting', $EnableErrorReporting) $paramDictionary.Add('LogDirectoryPath', $LogDirectoryPath) $paramDictionary.Add('LogLevel', $LogLevel) $paramDictionary.Add('Credential', $Credential) $paramDictionary.Add('InlineCredential', $InlineCredential) return $paramDictionary } } process { # Validate parameters if (($ConnectionUri -ne '') -and (-not (Test-Uri $ConnectionUri))) { throw "Invalid ConnectionUri parameter '$ConnectionUri'" } if (($AzureADAuthorizationEndpointUri -ne '') -and (-not (Test-Uri $AzureADAuthorizationEndpointUri))) { throw "Invalid AzureADAuthorizationEndpointUri parameter '$AzureADAuthorizationEndpointUri'" } if (($Prefix -ne '')) { if ($Prefix -notmatch '^[a-z0-9]+$') { throw "Use of any special characters in the Prefix string is not supported." } if ($Prefix -eq 'EXO') { throw "Prefix 'EXO' is a reserved Prefix, please use a different prefix." } } if ($ShowBanner -eq $true) { Print-Details; } if (($ConnectionUri -ne '') -and ($AzureADAuthorizationEndpointUri -eq '')) { Write-Host -ForegroundColor Green "Using ConnectionUri:'$ConnectionUri', in the environment:'$ExchangeEnvironmentName'." } if (($AzureADAuthorizationEndpointUri -ne '') -and ($ConnectionUri -eq '')) { Write-Host -ForegroundColor Green "Using AzureADAuthorizationEndpointUri:'$AzureADAuthorizationEndpointUri', in the environment:'$ExchangeEnvironmentName'." } # Keep track of error count at beginning. $errorCountAtStart = $global:Error.Count; try { Write-Verbose "ExchangeEnvironment : $ExchangeEnvironmentName" Write-Verbose "ConnectionUri : $ConnectionUri" Write-Verbose "AzureADAuthorizationEndpointUri : $AzureADAuthorizationEndpointUri" Write-Verbose "DelegatedOrganization : $DelegatedOrganization" Write-Verbose "Prefix : $Prefix" Write-Verbose ("FormatTypeName :" + [String]::Join(' ,', $FormatTypeName)) Write-Verbose ("CommandName :" + [String]::Join(' ,', $CommandName)) if ($isCloudShell -eq $false) { $ConnectionContext = Get-ConnectionContext -ExchangeEnvironmentName $ExchangeEnvironmentName -ConnectionUri $ConnectionUri -AzureADAuthorizationEndpointUri $AzureADAuthorizationEndpointUri -UserPrincipalName $UserPrincipalName.Value -PSSessionOption $PSSessionOption -Credential $Credential.Value -BypassMailboxAnchoring:$BypassMailboxAnchoring -DelegatedOrg $DelegatedOrganization -Certificate $Certificate.Value -CertificateFilePath $CertificateFilePath.Value -CertificatePassword $CertificatePassword.Value -CertificateThumbprint $CertificateThumbprint.Value -AppId $AppId.Value -Organization $Organization.Value -Device:$Device.Value -InlineCredential:$InlineCredential.Value -CommandName $CommandName -FormatTypeName $FormatTypeName -Prefix $Prefix -PageSize $PageSize.Value } else { $ConnectionContext = Get-ConnectionContext -ExchangeEnvironmentName $ExchangeEnvironmentName -ConnectionUri $ConnectionUri -AzureADAuthorizationEndpointUri $AzureADAuthorizationEndpointUri -Credential $Credential.Value -PSSessionOption $PSSessionOption -BypassMailboxAnchoring:$BypassMailboxAnchoring -Device:$Device.Value -InlineCredential:$InlineCredential.Value -DelegatedOrg $DelegatedOrganization -CommandName $CommandName -FormatTypeName $FormatTypeName -Prefix $prefix } if ($isCloudShell -eq $false) { $global:_EXO_EnableErrorReporting = $EnableErrorReporting.Value; } $ImportedModuleName = ''; $LogModuleDirectoryPath = [System.IO.Path]::GetTempPath(); if ($UseRPSSession -eq $true) { $ExoPowershellModule = "Microsoft.Exchange.Management.ExoPowershellGalleryModule.dll"; $ModulePath = [System.IO.Path]::Combine($PSScriptRoot, $ExoPowershellModule); Import-Module $ModulePath; $global:_EXO_ModulePath = $ModulePath; $PSSession = New-ExoPSSession -ConnectionContext $ConnectionContext if ($PSSession -ne $null) { $PSSessionModuleInfo = Import-PSSession $PSSession -AllowClobber -DisableNameChecking -CommandName $CommandName -FormatTypeName $FormatTypeName # Add the name of the module to clean up in case of removing the broken session $PSSession | Add-Member -NotePropertyName "CurrentModuleName" -NotePropertyValue $PSSessionModuleInfo.Name # Import the above module globally. This is needed as with using psm1 files, # any module which is dynamically loaded in the nested module does not reflect globally. $CurrentModule = Import-Module $PSSessionModuleInfo.Path -Global -DisableNameChecking -Prefix $Prefix -PassThru $CurrentModule | Add-Member -NotePropertyName "ModulePrefix" -NotePropertyValue $Prefix $CurrentModule | Add-Member -NotePropertyName "CommandName" -NotePropertyValue $CommandName $CurrentModule | Add-Member -NotePropertyName "FormatTypeName" -NotePropertyValue $FormatTypeName UpdateImplicitRemotingHandler # Import the REST module $RestPowershellModule = "Microsoft.Exchange.Management.RestApiClient.dll"; $RestModulePath = [System.IO.Path]::Combine($PSScriptRoot, $RestPowershellModule); Import-Module $RestModulePath -Cmdlet Set-ExoAppSettings; $ImportedModuleName = $PSSessionModuleInfo.Name; } } else { # Download the new web based EXOModule $ImportedModule = New-EXOModule -ConnectionContext $ConnectionContext; if ($null -ne $ImportedModule) { $ImportedModuleName = $ImportedModule.Name; $LogModuleDirectoryPath = $ImportedModule.ModuleBase Write-Verbose "AutoGen EXOModule created at $($ImportedModule.ModuleBase)" if ($null -ne $HelpFileNames -and $HelpFileNames -is [array] -and $HelpFileNames.Count -gt 0) { Get-HelpFiles -HelpFileNames $HelpFileNames -ConnectionContext $ConnectionContext -ImportedModule $ImportedModule -EnableErrorReporting:$EnableErrorReporting.Value } else { Write-Warning "Get-help cmdlet might not work. Please reconnect if you want to access that functionality." } } else { throw "Module could not be correctly formed. Please run Connect-ExchangeOnline again." } } # If we are configured to collect telemetry, add telemetry wrappers. if ($EnableErrorReporting.Value -eq $true) { $FilePath = Add-EXOClientTelemetryWrapper -Organization (Get-OrgNameFromUPN -UPN $UserPrincipalName.Value) -PSSessionModuleName $ImportedModuleName -LogDirectoryPath $LogDirectoryPath.Value -LogModuleDirectoryPath $LogModuleDirectoryPath $global:_EXO_TelemetryFilePath = $FilePath[0] Import-Module $FilePath[1] -DisableNameChecking -Global Push-EXOTelemetryRecord -TelemetryFilePath $global:_EXO_TelemetryFilePath -CommandName Connect-ExchangeOnline -CommandParams $PSCmdlet.MyInvocation.BoundParameters -OrganizationName $global:_EXO_ExPSTelemetryOrganization -ScriptName $global:_EXO_ExPSTelemetryScriptName -ScriptExecutionGuid $global:_EXO_ExPSTelemetryScriptExecutionGuid if ($EOPConnectionInProgress -eq $false) { # Set the AppSettings Set-ExoAppSettings -ShowProgress $ShowProgress.Value -PageSize $PageSize.Value -UseMultithreading $UseMultithreading.Value -TrackPerformance $TrackPerformance.Value -EnableErrorReporting $true -LogDirectoryPath $LogDirectoryPath.Value -LogLevel $LogLevel.Value } } else { if ($EOPConnectionInProgress -eq $false) { # Set the AppSettings disabling the logging Set-ExoAppSettings -ShowProgress $ShowProgress.Value -PageSize $PageSize.Value -UseMultithreading $UseMultithreading.Value -TrackPerformance $TrackPerformance.Value -EnableErrorReporting $false } } } catch { # If telemetry is enabled, log errors generated from this cmdlet also. if ($EnableErrorReporting.Value -eq $true) { $errorCountAtProcessEnd = $global:Error.Count if ($global:_EXO_TelemetryFilePath -eq $null) { $global:_EXO_TelemetryFilePath = New-EXOClientTelemetryFilePath -LogDirectoryPath $LogDirectoryPath.Value # Import the REST module $RestPowershellModule = "Microsoft.Exchange.Management.RestApiClient.dll"; $RestModulePath = [System.IO.Path]::Combine($PSScriptRoot, $RestPowershellModule); Import-Module $RestModulePath -Cmdlet Set-ExoAppSettings; # Set the AppSettings Set-ExoAppSettings -ShowProgress $ShowProgress.Value -PageSize $PageSize.Value -UseMultithreading $UseMultithreading.Value -TrackPerformance $TrackPerformance.Value -ExchangeEnvironmentName $ExchangeEnvironmentName -ConnectionUri $ConnectionUri -AzureADAuthorizationEndpointUri $AzureADAuthorizationEndpointUri -EnableErrorReporting $true -LogDirectoryPath $LogDirectoryPath.Value -LogLevel $LogLevel.Value } # Log errors which are encountered during Connect-ExchangeOnline execution. Write-Warning("Writing Connect-ExchangeOnline error log to " + $global:_EXO_TelemetryFilePath) Push-EXOTelemetryRecord -TelemetryFilePath $global:_EXO_TelemetryFilePath -CommandName Connect-ExchangeOnline -CommandParams $PSCmdlet.MyInvocation.BoundParameters -OrganizationName $global:_EXO_ExPSTelemetryOrganization -ScriptName $global:_EXO_ExPSTelemetryScriptName -ScriptExecutionGuid $global:_EXO_ExPSTelemetryScriptExecutionGuid -ErrorObject $global:Error -ErrorRecordsToConsider ($errorCountAtProcessEnd - $errorCountAtStart) } throw $_ } } } function Connect-IPPSSession { [CmdletBinding()] param( # Connection Uri for the Remote PowerShell endpoint [string] $ConnectionUri = 'https://ps.compliance.protection.outlook.com/PowerShell-LiveId', # Azure AD Authorization endpoint Uri that can issue the OAuth2 access tokens [string] $AzureADAuthorizationEndpointUri = 'https://login.microsoftonline.com/organizations', # Delegated Organization Name [string] $DelegatedOrganization = '', # PowerShell session options to be used when opening the Remote PowerShell session [System.Management.Automation.Remoting.PSSessionOption] $PSSessionOption = $null, # Switch to bypass use of mailbox anchoring hint. [switch] $BypassMailboxAnchoring = $false, # Prefix [string] $Prefix = '', #Cmdlets to Import, by default it would bring all [string[]] $CommandName = @("*"), #The way the output objects would be printed on the console [string[]] $FormatTypeName = @("*") ) DynamicParam { if (($isCloudShell = IsCloudShellEnvironment) -eq $false) { $attributes = New-Object System.Management.Automation.ParameterAttribute $attributes.Mandatory = $false $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $attributeCollection.Add($attributes) # User Principal Name or email address of the user $UserPrincipalName = New-Object System.Management.Automation.RuntimeDefinedParameter('UserPrincipalName', [string], $attributeCollection) $UserPrincipalName.Value = '' # User Credential to Logon $Credential = New-Object System.Management.Automation.RuntimeDefinedParameter('Credential', [System.Management.Automation.PSCredential], $attributeCollection) $Credential.Value = $null $paramDictionary = New-object System.Management.Automation.RuntimeDefinedParameterDictionary $paramDictionary.Add('UserPrincipalName', $UserPrincipalName) $paramDictionary.Add('Credential', $Credential) return $paramDictionary } else { $attributes = New-Object System.Management.Automation.ParameterAttribute $attributes.Mandatory = $false $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $attributeCollection.Add($attributes) # Switch to MSI auth $Device = New-Object System.Management.Automation.RuntimeDefinedParameter('Device', [switch], $attributeCollection) $Device.Value = $false $paramDictionary = New-object System.Management.Automation.RuntimeDefinedParameterDictionary $paramDictionary.Add('Device', $Device) return $paramDictionary } } process { try { $EOPConnectionInProgress = $true if ($isCloudShell -eq $false) { Connect-ExchangeOnline -ConnectionUri $ConnectionUri -AzureADAuthorizationEndpointUri $AzureADAuthorizationEndpointUri -UserPrincipalName $UserPrincipalName.Value -PSSessionOption $PSSessionOption -Credential $Credential.Value -BypassMailboxAnchoring:$BypassMailboxAnchoring -ShowBanner:$false -DelegatedOrganization $DelegatedOrganization -Prefix $Prefix -CommandName $CommandName -FormatTypeName $FormatTypeName -UseRPSSession:$true } else { Connect-ExchangeOnline -ConnectionUri $ConnectionUri -AzureADAuthorizationEndpointUri $AzureADAuthorizationEndpointUri -PSSessionOption $PSSessionOption -BypassMailboxAnchoring:$BypassMailboxAnchoring -Device:$Device.Value -ShowBanner:$false -DelegatedOrganization $DelegatedOrganization -Prefix $Prefix -CommandName $CommandName -FormatTypeName $FormatTypeName -UseRPSSession:$true } } finally { $EOPConnectionInProgress = $false } } } function Disconnect-ExchangeOnline { [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')] param() process { if ($PSCmdlet.ShouldProcess( "Running this cmdlet clears all active sessions created using Connect-ExchangeOnline or Connect-IPPSSession.", "Press(Y/y/A/a) if you want to continue.", "Running this cmdlet clears all active sessions created using Connect-ExchangeOnline or Connect-IPPSSession. ")) { # Keep track of error count at beginning. $errorCountAtStart = $global:Error.Count; try { # Import the module once more to ensure that Test-ActiveToken is present $ExoPowershellModule = "Microsoft.Exchange.Management.ExoPowershellGalleryModule.dll"; $ModulePath = [System.IO.Path]::Combine($PSScriptRoot, $ExoPowershellModule); Import-Module $ModulePath -Cmdlet Clear-ActiveToken; $existingPSSession = Get-PSSession | Where-Object {$_.ConfigurationName -like "Microsoft.Exchange" -and $_.Name -like "ExchangeOnlineInternalSession*"} if ($existingPSSession.count -gt 0) { for ($index = 0; $index -lt $existingPSSession.count; $index++) { $session = $existingPSSession[$index] Remove-PSSession -session $session Write-Host "Removed the PSSession $($session.Name) connected to $($session.ComputerName)" # Remove any active access token from the cache Clear-ActiveToken -TokenProvider $session.TokenProvider # Remove any previous modules loaded because of the current PSSession if ($session.PreviousModuleName -ne $null) { if ((Get-Module $session.PreviousModuleName).Count -ne 0) { Remove-Module -Name $session.PreviousModuleName -ErrorAction SilentlyContinue } $session.PreviousModuleName = $null } # Remove any leaked module in case of removal of broken session object if ($session.CurrentModuleName -ne $null) { if ((Get-Module $session.CurrentModuleName).Count -ne 0) { Remove-Module -Name $session.CurrentModuleName -ErrorAction SilentlyContinue } } } } # Remove all the AutoREST modules from this instance of powershell if created $existingAutoRESTModules = Get-Module "tmpEXO_*" foreach ($module in $existingAutoRESTModules) { Remove-Module -Name $module -ErrorAction SilentlyContinue } Write-Host "Disconnected successfully !" if ($global:_EXO_EnableErrorReporting -eq $true) { # Remove all the Wrapped modules from this instance of powershell if created $existingWrappedModules = Get-Module "EXOCmdletWrapper-*" foreach ($module in $existingWrappedModules) { Remove-Module -Name $module -ErrorAction SilentlyContinue } if ($global:_EXO_TelemetryFilePath -eq $null) { $global:_EXO_TelemetryFilePath = New-EXOClientTelemetryFilePath } Push-EXOTelemetryRecord -TelemetryFilePath $global:_EXO_TelemetryFilePath -CommandName Disconnect-ExchangeOnline -CommandParams $PSCmdlet.MyInvocation.BoundParameters -OrganizationName $global:_EXO_ExPSTelemetryOrganization -ScriptName $global:_EXO_ExPSTelemetryScriptName -ScriptExecutionGuid $global:_EXO_ExPSTelemetryScriptExecutionGuid } } catch { # If telemetry is enabled, log errors generated from this cmdlet also. if ($global:_EXO_EnableErrorReporting -eq $true) { $errorCountAtProcessEnd = $global:Error.Count if ($global:_EXO_TelemetryFilePath -eq $null) { $global:_EXO_TelemetryFilePath = New-EXOClientTelemetryFilePath } # Log errors which are encountered during Disconnect-ExchangeOnline execution. Write-Warning("Writing Disconnect-ExchangeOnline errors to " + $global:_EXO_TelemetryFilePath) Push-EXOTelemetryRecord -TelemetryFilePath $global:_EXO_TelemetryFilePath -CommandName Disconnect-ExchangeOnline -CommandParams $PSCmdlet.MyInvocation.BoundParameters -OrganizationName $global:_EXO_ExPSTelemetryOrganization -ScriptName $global:_EXO_ExPSTelemetryScriptName -ScriptExecutionGuid $global:_EXO_ExPSTelemetryScriptExecutionGuid -ErrorObject $global:Error -ErrorRecordsToConsider ($errorCountAtProcessEnd - $errorCountAtStart) } throw $_ } } } } # SIG # Begin signature block # MIIjkQYJKoZIhvcNAQcCoIIjgjCCI34CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC8AVxYLkSBXYiI # 2VXTeDYpPkaUUhgKuMgPNpNbdxIjOqCCDYEwggX/MIID56ADAgECAhMzAAAB32vw # LpKnSrTQAAAAAAHfMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAxMjE1MjEzMTQ1WhcNMjExMjAyMjEzMTQ1WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQC2uxlZEACjqfHkuFyoCwfL25ofI9DZWKt4wEj3JBQ48GPt1UsDv834CcoUUPMn # s/6CtPoaQ4Thy/kbOOg/zJAnrJeiMQqRe2Lsdb/NSI2gXXX9lad1/yPUDOXo4GNw # PjXq1JZi+HZV91bUr6ZjzePj1g+bepsqd/HC1XScj0fT3aAxLRykJSzExEBmU9eS # yuOwUuq+CriudQtWGMdJU650v/KmzfM46Y6lo/MCnnpvz3zEL7PMdUdwqj/nYhGG # 3UVILxX7tAdMbz7LN+6WOIpT1A41rwaoOVnv+8Ua94HwhjZmu1S73yeV7RZZNxoh # EegJi9YYssXa7UZUUkCCA+KnAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUOPbML8IdkNGtCfMmVPtvI6VZ8+Mw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDYzMDA5MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAnnqH # tDyYUFaVAkvAK0eqq6nhoL95SZQu3RnpZ7tdQ89QR3++7A+4hrr7V4xxmkB5BObS # 0YK+MALE02atjwWgPdpYQ68WdLGroJZHkbZdgERG+7tETFl3aKF4KpoSaGOskZXp # TPnCaMo2PXoAMVMGpsQEQswimZq3IQ3nRQfBlJ0PoMMcN/+Pks8ZTL1BoPYsJpok # t6cql59q6CypZYIwgyJ892HpttybHKg1ZtQLUlSXccRMlugPgEcNZJagPEgPYni4 # b11snjRAgf0dyQ0zI9aLXqTxWUU5pCIFiPT0b2wsxzRqCtyGqpkGM8P9GazO8eao # mVItCYBcJSByBx/pS0cSYwBBHAZxJODUqxSXoSGDvmTfqUJXntnWkL4okok1FiCD # Z4jpyXOQunb6egIXvkgQ7jb2uO26Ow0m8RwleDvhOMrnHsupiOPbozKroSa6paFt # VSh89abUSooR8QdZciemmoFhcWkEwFg4spzvYNP4nIs193261WyTaRMZoceGun7G # CT2Rl653uUj+F+g94c63AhzSq4khdL4HlFIP2ePv29smfUnHtGq6yYFDLnT0q/Y+ # Di3jwloF8EWkkHRtSuXlFUbTmwr/lDDgbpZiKhLS7CBTDj32I0L5i532+uHczw82 # oZDmYmYmIUSMbZOgS65h797rj5JJ6OkeEUJoAVwwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZjCCFWICAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAd9r8C6Sp0q00AAAAAAB3zAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgrZoZre9d # QZ7LDcmY6ykQrU+chqGRkm6VLj9Zd2sGC6IwQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQAI0KWnp1ojbb7TFK3yZCOw2LnHSKlaqmjxZXzVQPVD # JhcVBFF5w9lkBEMjWm1dFoOWhHK4NQQlje3UKoUzueNRqgppBZgDAoaRAeAj3fHy # 68CcMmz7DDx6nu+aYmTf33bKD3bitSDQKLGBUTHxKUtMEPxwoICS7QnHAe9ZV3rn # cgJlIEZ9sR7JlbrLevZr9zLyde5uUrRt/4ArpovrBKtWfbjICuBrqsHjCW34CUk+ # 7jbzOgNofIJxsmAEwL+nbvzZAHr+7YPQgUNh+YSkGhX6kDAbInVqkZdo8LrYQWo6 # xDQiB9tWKUVMzaqwJCdWQAkfw8ochDQFiuC07zMco0CToYIS8DCCEuwGCisGAQQB # gjcDAwExghLcMIIS2AYJKoZIhvcNAQcCoIISyTCCEsUCAQMxDzANBglghkgBZQME # AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIDQb/g6DaNbgmqjDLBidJBZr5WZ8OeiPNxZN5dyO # jj7GAgZhb0b8490YEzIwMjExMDIwMDIxNzAwLjI4N1owBIACAfSggdSkgdEwgc4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p # Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg # VFNTIEVTTjpGNzdGLUUzNTYtNUJBRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt # U3RhbXAgU2VydmljZaCCDkMwggT1MIID3aADAgECAhMzAAABXp0px1+HBaHqAAAA # AAFeMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw # MB4XDTIxMDExNDE5MDIxOVoXDTIyMDQxMTE5MDIxOVowgc4xCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy # YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpGNzdG # LUUzNTYtNUJBRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj # ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJrTI4OQehn3oKp2zuh6 # WP2Zib/Dxw/srLeeyTb9ed7PX+fLg7zBA0yl6ivF2n6lauGH8W7EBwRPEv7ZCSXw # XgYZ6GGaH8aU+OrDXAbc4BXTO5XnLGwSbaye9R2+uQHdCJmaMtz/lEBWUK5xvHoj # 0TUrXOZdZ/vv7TqMWA4h1AT1w/JBR4kHtV1i8KWdlQ+dZX/gNHpA72IoLoOmpImb # GRzcGQ4Z2Kzq4eMB9wjaFRV1JF/wz1hLFIjGtlU3eGjRBiBEEVI7UEMMSvI4rK+C # fLAIZnULu7SzlIfqSU3R0pSNUahwpWdCiB6fKzIq94Z+9888moQuo95RAPmzHQW1 # MI0CAwEAAaOCARswggEXMB0GA1UdDgQWBBSqcny6Dd1L5VTCEACezlR41fgfKzAf # BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH # hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU # aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF # BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0 # YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG # AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQB/IfxZhMYkBMqRmXnh/Vit4bfxyioA # lr7HJ1XDSHTIvRwDD1PGr0upZE/vrrI/QN/1Wi6vDcKmnJ2r7Xj6pWZOZqc9Bp+u # BvpPaulue4stu3TqKTc9Fu2K5ibctpF4oHPfZ+IKeChop+Mk9g7N5llHzv0aCDia # M0w2aAT3rj3QHQS8ijnQ5/qhtzwo1AoUnV1y2urWwX5aHdUzaoeAJrvnf2ee89Kf # 4ycjjyafNJSUp/qaXBlbjMu90vNubJstdSxOtvwcxeeHP6ZaYbTl2cOla4cokiPU # +BUjIZA/t/IZfYoazMGmBtLWFJZdC9LYWWmLLsNJ2W21qkeSSpEAw4pmMIIGcTCC # BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv # b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN # MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw # DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0 # VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw # RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe # dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx # Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G # kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA # AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7 # fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g # AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB # BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA # bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh # IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS # +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK # kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon # /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi # PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/ # fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII # YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0 # cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a # KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ # cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+ # NR4Iuto229Nfj950iEkSoYIC0TCCAjoCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP # cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpG # NzdGLUUzNTYtNUJBRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaIjCgEBMAcGBSsOAwIaAxUAVkmPV/8hZVS9FzbtoX2x3Z2xYyqggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # AOUZxXwwIhgPMjAyMTEwMjAwMjMwMjBaGA8yMDIxMTAyMTAyMzAyMFowdjA8Bgor # BgEEAYRZCgQBMS4wLDAKAgUA5RnFfAIBADAJAgEAAgEQAgH/MAcCAQACAhEoMAoC # BQDlGxb8AgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEA # AgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEABLmFf37/ZRiaNO+9 # kp5NPlRswgFNK0yq7487kwsWOSRL58utBbmefVLQUAcHHsl5wCDuLC3hK3C5Ihib # c6aUIFKf+wd66DSsnifb0CL7nJdpjy26gNkFjDnhlaC8fbDyBpGPFBmDJP3ArKfW # d1SAe2Regc2ZqXX0gSXvJ7y/QnQxggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGlt # ZS1TdGFtcCBQQ0EgMjAxMAITMwAAAV6dKcdfhwWh6gAAAAABXjANBglghkgBZQME # AgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJ # BDEiBCDT14p/+4Mtyq7Naem7GyNY7jDCDawifDXpro/9GtzOJDCB+gYLKoZIhvcN # AQkQAi8xgeowgecwgeQwgb0EIH7lhOyU1JeO4H7mZANMpGQzumuR7CFed69eku/x # EtPiMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAFe # nSnHX4cFoeoAAAAAAV4wIgQgklKFm1thJZBrkZL52Z9mrdmUhJmchNiECbw2hZo3 # hHowDQYJKoZIhvcNAQELBQAEggEAPom8akpWavIAkjLsnKwXMGc8eLvMg9iBTL/6 # 5tCy2nYVjsR2EKn/H1Sol77sMX7by3PLE3YGUb35PjH/mblApnLp4gan428GW6Q3 # UcrFgj64BCU8Mj5Udsdoh1e3ltPPAgRFj98KPBxAQOTcSUIcozoLppQcKTAmWfwn # KKeiXcszJszGDV0OifOAd4D86RiXuBdMUU6wDgtlX1dE1/28MJ69RkDlRbdA45By # O/Ak+WTY4v2E0wc0rNw+3ep30tRKBQPgb7jb6sh3sGQ/CEMau5ckIyVi3HP9a8lP # doWnREyDBfqwNyTLwnM1u6AnF53PK8mFyX28+Dg3umXlp7IwOg== # SIG # End signature block |