Public/Common.ps1
<#
.SYNOPSIS Detects the SEPPmail.Cloud Deploymemnt status, based in M365 Tenant information .DESCRIPTION Checks Deplyoment Status of the SEPPmail.Cloud based on M365 Tenantinformation from outside, based on DNS information - Queries routingmode based on available MX records in the SEPPmail.cloud - Queries region based on IP adresses and mailhosts - Tests if MX record is set correctly in Inline Mode - Checks if the SEPPmail.cloud is prepared for Certificate-based-Connectors Creates a PSObject with the following values: Routing = inline/parallel # Routing Mode Region = ch/de/prv # Cloud Region (Datacenter location) SEPPmailCloudDomain = 'contoso.de','contoso.ch' # Maildomains which will be routet via SEPPmail. Is basis for naming the mailrouting hosts (gate/relay/mail) CBCenabled = $true/$false # Certificate Based Connectors setup available CBCConnectorHost = '<tenantid>.<rg>.seppmail.cloud' # Hostname of TLS host for CBC InlineMXMatch = $true/$false # (Inline Mod only) MX record points to the correct (SEPPmail) host RelayHost = domain-tls.relay.seppmail.cloud # Name of relay host GateHost = domain-tls.gate.seppmail.cloud # Name of gate host MailHost = domain-tls.mail.seppmail.cloud # Name of mail host .NOTES Emits parameters for New-SC365Setup .LINK https://github.com/seppmail/SEPPmail365cloud/blob/main/README.md .EXAMPLE Get-SC365DeploymentInfo DeployMentStatus : True SEPPmailCloudDomain : contoso.com Region : ch Routing : inline InBoundOnly : False CBCDeployed : True CBCConnectorHost : 271dd771-832d-4913-80d7-9c21616accd4.ch.seppmail.cloud CBCDnsEntry : c60abc9d247a2bf21cbc3344eef199eb738876b2.cbc.seppmail.cloud InlineMXMatch : True MailHost : RelayHost : contoso-com.relay.seppmail.cloud GateHost : contoso-com.gate.seppmail.cloud .EXAMPLE Get-SC365DeploymentInfo -SEPPmailCLoudDomain contoso.eu DeployMentStatus : True SEPPmailCloudDomain : contoso.eu Region : de Routing : inline InBoundOnly : False CBCDeployed : True CBCConnectorHost : 271dd771-832d-4913-80d7-9c21616accd4.de.seppmail.cloud CBCDnsEntry : c60abc9d247a2bf21cbc3344eef199eb738876b2.cbc.seppmail.cloud InlineMXMatch : True MailHost : RelayHost : contoso-eu.relay.seppmail.cloud GateHost : contoso-eu.gate.seppmail.cloud #> function Get-SC365DeploymentInfo { [CmdletBinding()] param ( [Parameter( Mandatory = $false, HelpMessage = "Domain name you selected for Tenant-onboarding" )] [string[]]$SEPPmailCloudDomain ) begin { if (!(Test-SC365ConnectionStatus)){ throw [System.Exception] "You're not connected to Exchange Online - please connect to the designated tenant prior to using this CmdLet" } else { Write-Information "Connected to Exchange Organization `"$Script:ExODefaultDomain`"" -InformationAction Continue } # Reset the Stage $DeployMentStatus = $null $region = $null $routing = $null $inBoundOnly = $null $DeploymentInfo = [PSCustomObject]@{ DeploymentStatus = $null SEPPmailCloudDomain = $Null Region = $null Routing = $null InboundOnly = $null CBCDeployed = $Null CBCConnectorHost = $null CBCDnsEntry = $null InlineMXMatch = $null MailHost = $null GateHost = $null RelayHost = $null SwissSignCheckTXT = $null spfTXT = $null DnsSecurEmailCNAME = $null DnsLetsEncryptCNAME = $null DnsDKIMTXT = $null DnsWildCardActive = $null } } process { #region Select DefaultDomain if (!($SEPPmailCloudDomain)) { [String]$DNSHostDomain = $tenantAcceptedDomains |Where-Object 'Default' -eq $true |select-Object -ExpandProperty DomainName Write-Verbose "Extracted Default-Domain with name $DNSHostDomain from TenantDefaultDomains" } else { foreach ($dom in $SEPPmailCloudDomain) { if (!($tenantAcceptedDomains.DomainName.Contains($Dom))) { throw [System.Exception] "Domain $Dom is not member of this tenant! Check for typos or connect to different tenant" } else { Write-Verbose "Domain $Dom is member of the tenant domains" if ($dom -eq ($tenantAcceptedDomains |Where-Object 'Default' -eq $true |select-Object -ExpandProperty DomainName)) { $DnsHostDomain = $dom } else { Write-Verbose "TenantDefaultDomain not selected, using $dom ans DNSHostDoman" $DnsHostDomain = $dom } } } } #endregion Select DefaultDomain #region Query SEPPmail routing-Hosts DNS records and detect routing mode and in/oitbound [string]$relayHost = $DnsHostDomain.Replace('.','-') + '.relay.seppmail.cloud' [string]$mailHost = $DnsHostDomain.Replace('.','-') + '.mail.seppmail.cloud' [string]$gateHost = $DnsHostDomain.Replace('.','-') + '.gate.seppmail.cloud' $defEAPref = $ErrorActionPreference $ErrorActionPreference = 'SilentlyContinue' $DeploymentStatus = 'unknown' if (((Resolve-Dns -Query $GateHost).Answers)) { $routing = 'inline' if ((Resolve-Dns -Query $RelayHost).Answers) { $inBoundOnly = $false Write-Verbose "$GateHost and $relayHost alive ==> InLine-bidirectional" $deploymentStatus = $true } else { if (!((Resolve-Dns -Query $RelayHost).Answers)) { $inBoundOnly = $true $relayHost = $null Write-Verbose "$GateHost alive,$relayHost missing ==> InLine-InBound only" $deploymentStatus = $true } } } else { if ((Resolve-Dns -Query $MailHost ).Answers) { Write-verbose "$mailHost alive ==> parallel" $routing = 'parallel' $deploymentStatus = $true } else { $mailHost = $null $deploymentStatus = $false } } #endregion Mailhost queries #region DoubleCheck if MX Record is set correctly $mxFull = get-mxrecordreport -Domain $DnsHostDomain if ($mxFull.Count -eq 0) { $DeployMentStatus = $false } else { if ($mxFull.Count -eq 1) { $mx = $mxFull | Select-Object -ExpandProperty highestpriorityMailHost -Unique } if ($mxFull.Count -gt 1) { $mx = $mxFull[0] | Select-Object -ExpandProperty highestpriorityMailHost -Unique } if (($mx.Split($DnsHostDomain.Replace('.', '-'))) -eq '.gate.seppmail.cloud') { Write-Verbose "MX = SEPPmail" } if (($mx.Split($DnsHostDomain.Replace('.', '-'))) -eq '.mail.protection.outlook.com') { Write-Verbose "MX = Microsoft" } if ($routing -eq 'inline') { if ($mx -eq $gateHost) { Write-Verbose "MX record $mx in M365 Config matches $gateHost" $mxMatch = $true } else { Write-Warning "MX Record $mx configured in M365 does not fit to SEPPmail GateHost $gateHost in DNS - Check your provisioning Status in SEPPmail.cloud Portal" $mxMatch = $false $DeployMentStatus = $false } } } #endRegion MX Check #region Identify region based on Cloud-IPAddresses $region = $null $ch = Get-SC365CloudConfig -region 'ch' $de = Get-SC365CloudConfig -region 'de' $prv = Get-SC365CloudConfig -region 'prv' $dev = Get-SC365CloudConfig -region 'dev' if ($routing -eq 'inline') { [String[]]$GateIP = ((Resolve-Dns -Query $GateHost).Answers)|Select-Object -expand Address| Select-Object -expand IPAddressToString Foreach ($IP in $GateIP) {if ($ch.IPv4GateIPs.Contains($Ip)) {$region = 'ch';break}} Foreach ($IP in $GateIP) {if ($de.IPv4GateIPs.Contains($Ip)) {$region = 'de';break}} Foreach ($IP in $GateIP) {if ($prv.IPv4GateIPs.Contains($Ip)) {$region = 'prv';break}} Foreach ($IP in $GateIP) {if ($dev.IPv4GateIPs.Contains($Ip)) {$region = 'dev';break}} } if ($routing -eq 'parallel') { [string[]]$MailIP = ((Resolve-Dns -Query $MailHost).Answers)|Select-Object -expand Address| Select-Object -expand IPAddressToString Foreach ($ip in $mailIp) {if ($ch.IPv4MailIPs.Contains($Ip)) { $region = 'ch';break}} Foreach ($ip in $mailIp) {if ($de.IPv4MailIPs.Contains($Ip)) { $region = 'de';break}} Foreach ($ip in $mailIp) {if ($prv.IPv4MailIPs.Contains($IP)) { $region = 'prv';break}} Foreach ($ip in $mailIp) {if ($dev.IPv4MailIPs.Contains($IP)) { $region = 'dev';break}} } #endregion Cloud-IP-Addresses #region Check CBC Availability [String]$TenantID = Get-SC365TenantID -MailDomain $DnsHostDomain -OutVariable "TenantID" $TenantIDHash = Get-SC365StringHash -String $TenantID [string]$hashedDomain = $TenantIDHash + '.cbc.seppmail.cloud' if (((resolve-dns -query $hashedDomain -QueryType TXT).Answers)) { $CBCDeployed = $true Write-Verbose "$HashedDomain of TenantID $tenantId has a CBC entry" } else { $CBCDeployed = $false Write-Warning "Could not find TXT Entry for TenantID $TenantID of domain $DNSHostCloudDomain. Setup will most likely fail! Go to the SEPPmail.cloud-portal and check the deployment status." } #endregion CBC #region Advanced DNS Queries # TXT records (Swissign check and SPF) $txtRecords = (Resolve-dns -querytype TXT $DNSHostdomain).Answers if ($txtRecords) { $swisssignTXTRecord = $txtRecords|Where-Object EscapedText -like 'swisssign-check*' if ($swissSignTXTRecord) { $swissSignCheck = $swissSignTXTRecord.EscapedText.Trim('{','}') } else { Write-Warning "Swisssign TXT (swissSign-check) record is missing - SC-CERT deployment will fail!" } $spfTXTrecord = $txtRecords|Where-Object EscapedText -like 'v=spf*' if ($spfTXTrecord) { $spf = $spfTXTrecord.EscapedText.Trim('{','}') } else { Write-Warning "SPF TXT (v=spf*) record is missing" } } ## WebService hosts [String]$SecurEmailCNAME = (Resolve-dns -querytype CNAME -Query ('securemail.' + $DNSHostdomain)).Answers.CanonicalName.Value ## Letsencrypt [String]$LetsEncryptCNAME = (Resolve-dns -querytype CNAME -Query ('_acme-challenge.securemail.' + $DNSHostdomain)).Answers.CanonicalName.Value ## DKIM (default._domainkey) [String]$dkimRecord = (resolve-dns -QueryType TXT -query ('default._domainkey.' + $DNSHostDomain)).Answers.EscapedText ## Wildcard records if ((resolve-dns -query ('jioak84-nlkjec.' + $DNSHostDomain)).Answers.EscapedText) { $WildcardRecord = $true } else { $WildcardRecord = $false } $ErrorActionPreference = $defEAPref #endregion } end { $DeploymentInfo.DeploymentStatus = $DeploymentStatus $DeploymentInfo.Region = $region $DeploymentInfo.Routing = $routing $DeploymentInfo.InboundOnly = $inBoundOnly $DeploymentInfo.SEPPmailCloudDomain = $DNSHostDomain $DeploymentInfo.CBCDeployed = $CBCDeployed if ($region) {$DeploymentInfo.CBCConnectorHost = ($tenantId + ((Get-Variable $region).Value.TlsCertificate).Replace('*',''))} if ($CBCDeployed -eq $true) {$DeploymentInfo.CBCDnsEntry = ($TenantIDHash + '.cbc.seppmail.cloud')} if ($routing -eq 'inline') {$DeploymentInfo.InlineMXMatch = $MxMatch} if (($routing -eq 'inline') -and (!($inBoundOnly))) {$DeploymentInfo.RelayHost = $relayHost} if ($routing -eq 'inline') {$DeploymentInfo.GateHost = $gateHost} if ($routing -eq 'parallel') {$DeploymentInfo.MailHost = $MailHost} # DNS Records $DeploymentInfo.swisssignCheckTXT = $swisssignCheck $DeploymentInfo.spfTXT = $spf $DeploymentInfo.DnsSecurEmailCNAME = $SecurEmailCNAME $DeploymentInfo.DnsLetsEncryptCNAME = $LetsEncryptCNAME $DeploymentInfo.DnsDKIMTXT = $DKIMRecord $DeploymentInfo.DnsWildCardActive = $wildcardRecord return $DeploymentInfo } } <# .SYNOPSIS Removes all Rules and Connectors .DESCRIPTION Based on autodiscovery, or forced values through parameters, Remove-SC365Setup removes all connectors and rules from an Exo-Tenant .NOTES - none - .LINK https://github.com/seppmail/seppmail365cloud .EXAMPLE Remove-SC365Setup # Without any parameters, it runs discovery mode and removes rules and connectors .EXAMPLE Remove-SC365Setup -parallel # Forces to remove parallel setup config .EXAMPLE Remove-SC365Setup -inline # Forces to remove inline setup config .EXAMPLE Remove-SC365Setup -inline -inBoundOnly # Forces to remove inline in "InbohndOnly" mode setup config #> function Remove-SC365Setup { [CmdletBinding( SupportsShouldProcess = $true, ConfirmImpact = 'Medium', DefaultParameterSetName='parallel' )] param( [Parameter( ParameterSetName = 'parallel', Mandatory=$false, HelpMessage="Inline routing via SEPPmail (MX ==> SEPPmail), or routing via Microsoft (MX ==> Microsoft)" )] [ValidateNotNullOrEmpty()] [ValidateSet('parallel','inline','p','i')] [Parameter( ParameterSetName = 'inline', Mandatory=$false, HelpMessage="Inline routing via SEPPmail (MX ==> SEPPmail), or routing via Microsoft (MX ==> Microsoft)" )] [ValidateNotNullOrEmpty()] [ValidateSet('parallel','inline','p','i')] [String]$routing, [Parameter( ParameterSetName = 'inline', Mandatory=$false, HelpMessage="No routing of outbound traffic via SEPPmail.cloud" )] [switch]$InBoundOnly ) Begin { if(!(Test-SC365ConnectionStatus)) { throw [System.Exception] "You're not connected to Exchange Online - please connect prior to using this CmdLet" } else { if ((!($InboundOnly)) -or (!($routing)) ) { try { $deploymentInfo = Get-SC365DeploymentInfo } catch { Throw [System.Exception] "Could not autodetect SEPPmail.cloud Deployment Status, use manual parameters" } if ($DeploymentInfo.DeployMentStatus -eq $false) { Write-Error "SEPPmail.cloud setup not (fully) deployed. Use Cloud-Portal and fix deployment." break } else { if ($Deploymentinfo) { if ($deploymentInfo.Routing) {$Routing = $deploymentInfo.Routing} else {Write-Error "Cloud not autodetect routig info, use manual parameters"; break} if ($DeploymentInfo.inBoundOnly -eq $true) {$inboundOnly = $true} if ($DeploymentInfo.inBoundOnly -eq $false) {$inboundOnly = $false} if ($null -eq $DeploymentInfo.inBoundOnly) {$inboundOnly = $false} } } } else { if ($deploymentInfo.routing -eq 'p') {$routing = 'parallel'} if ($deploymentInfo.routing -eq 'i') {$routing = 'inline'} } } } Process { Write-Verbose "Creating Progress Bar" $objectCount = $null # Count Rules foreach ($file in (Get-Childitem -Path "$psscriptroot\..\ExOConfig\Rules\")) { $objectCount += if ((Get-SC365TransportRuleSettings -routing $routing -file $file).count -gt 0) {1} } # Count Connectors #if ((Get-SC365InboundConnectorSettings -routing $routing -file $file).count -gt 0) {1} $objectCount += 2 try { if ($InBoundOnly) { #Write-Progress -Activity "Removing SEPPmail.Cloud Setup" -Status "Removing Rules" -PercentComplete (0) Write-Information '--- Remove connector(s) ---' -InformationAction Continue Remove-SC365Connectors -routing $routing -Inboundonly:$inboundonly } else { #Write-Progress -Activity "Removing SEPPmail.Cloud Setup" -Status "Removing Rules" -PercentComplete (0) Write-Information '--- Removing transport rules ---' -InformationAction Continue Remove-SC365Rules Write-Information '--- Remove connector(s) ---' -InformationAction Continue Remove-SC365Connectors -routing $routing -Inboundonly:$inboundonly } } catch { throw [System.Exception] "Error: $($_.Exception.Message)" break } } End{ Write-Information "--- Successfully removed SEPPmail.cloud Setup in $routing mode ---" -InformationAction Continue } } <# .SYNOPSIS Creates all Rules and Connectors for SEPPmail.cloud .DESCRIPTION Based on autodiscovery, or forced values through parameters, New-SC365Setup creates all connectors and rules for an Exo-Tenant .NOTES - none - .LINK https://github.com/seppmail/seppmail365cloud .EXAMPLE New-SC365Setup # Without any parameters, it runs discovery mode and created rules and connectors .EXAMPLE New-SC365Setup -force # The force parameter will force the removal of an existig setup and recreate connectors and rules .EXAMPLE New-SC365Setup -SEPPmailCloudDomain contoso.com -routing parallel -region ch # Creates a setup for one domain in parallel mode and in region Switzerland .EXAMPLE New-SC365Setup -SEPPmailCloudDomain contoso.de -routing inline -region de # Creates a setup for one domain in inline mode and in region Germany/EU .EXAMPLE New-SC365Setup -SEPPmailCloudDomain contoso.de -routing inline -region de -inboundonly # Creates a setup for one domain in inline mode and in region Germany/EU inbound only. #> function New-SC365Setup { [CmdLetBinding( SupportsShouldProcess=$true, ConfirmImpact = 'Medium', HelpURI = 'https://github.com/seppmail/SEPPmail365cloud/blob/main/README.md' )] # Specifies a path to one or more locations. param( [Parameter( Mandatory=$false, HelpMessage="The primary domain, booked in the SEPPmail.cloud" )] [Alias('domain')] [ValidateNotNullOrEmpty()] [String[]]$SEPPmailCloudDomain, [Parameter( Mandatory=$false, HelpMessage="Inline routing via SEPPmail (MX ==> SEPPmail), or routing via Microsoft (MX ==> Microsoft)" )] [ValidateNotNullOrEmpty()] [ValidateSet('parallel','inline','p','i')] [String]$routing, [Parameter( Mandatory=$false, HelpMessage="Physical location of your data" )] [ValidateSet('dev','prv','de','ch')] [String]$region, [Parameter( Mandatory=$false, HelpMessage="No routing of outbound traffic via SEPPmail.cloud" )] [switch]$InBoundOnly, [Parameter( Mandatory=$false, HelpMessage="Removes existing setup" )] [switch]$force ) Begin { if(!(Test-SC365ConnectionStatus)) { throw [System.Exception] "You're not connected to Exchange Online - please connect prior to using this CmdLet" } else { Write-Verbose "Connected to Exchange Organization `"$Script:ExODefaultDomain`"" -InformationAction Continue } # If user tries to use *.onmicrosoft.com domain ==> BREAK if ($SEPPmailCloudDomain -like '*.onmicrosoft.com') { Write-Error "Domain $SEPPmailcloudDomain is not intended for E-Mail sending and cannot be booked for the SEPPmail-cloud Service. Specify a custom domain of your tenant and retry." break } Write-Verbose "Detecting Deploymentstatus frpm SEPPmail.cloud setup" try { $deploymentInfo = Get-SC365DeploymentInfo } catch { Throw [System.Exception] "Could not autodetect SEPPmail.cloud deployment status, check SEPPmail.cloud portal deployment status" } Write-Verbose "Not enough parameters given, reading from Tenant, otherwise use data from console" if ((!($SEPPmailCloudDomain)) -or (!($region)) -or (!($routing)) ) { # Customers where TDAD is set to *.onmicrosoft.com ==> BREAK if ($DeploymentInfo.SEPPmailCloudDomain -like '*.onmicrosoft.com') { Write-Error "Domain $($DeploymentInfo.SEPPmailCloudDomain) is set as the tenant default accepted domain. $($DeploymentInfo.SEPPmailCloudDomain) is not intended for E-Mail sending and cannot be booked for the SEPPmail-cloud Service. Specify a custom domain of your tenant and retry or change the Default accepted domain in your Exchange Online tenant." break } if ($DeploymentInfo.DeployMentStatus -eq $false) { Write-Error "SEPPmail.cloud setup for domain $deploymentinfo.SEPPmailCloudDomain is not (fully) deployed. Use Cloud-Portal and fix deployment." break } else { if ($Deploymentinfo) { if ($deploymentInfo.Routing) {$Routing = $deploymentInfo.Routing} else {Write-Error "Cloud not autodetect routig info, use manual parameters"; break} if ($deploymentInfo.Routing -ne $routing) {Write-Error "SEPPmail.cloud is deployed with routing $deploymentInfo.Routing but the routing parameter is set to $routing, this will NOT WORK, exiting ..."; break} if ($deploymentInfo.Region) {$Region = $deploymentInfo.Region} else {Write-Error "Could not autodetect region. Use manual parameters"; break} if ($deploymentInfo.Region -ne $region) {Write-Error "SEPPmail.cloud is deployed in region $deploymentInfo.Region but the region parameter is set to $region, this will NOT WORK, exiting ..."; break} if ($DeploymentInfo.SEPPmailCloudDomain) {$SEPPmailCloudDomain = $DeploymentInfo.SEPPmailCloudDomain} else {Write-Error "Could not autodetect SEPPmailCloudDomain. Use manual parameters"; break} if ($DeploymentInfo.inBoundOnly -eq $true) {$inboundOnly = $true} if ($DeploymentInfo.inBoundOnly -eq $false) {$inboundOnly = $false} if ($null -eq $DeploymentInfo.inBoundOnly) {$inboundOnly = $false} } } } else { if ($deploymentInfo.routing -eq 'p') {$routing = 'parallel'} if ($deploymentInfo.routing -eq 'i') {$routing = 'inline'} Write-Verbose "Checking if console parameter fit to deployment Info" if ($SEPPmailCloudDomain -ne $deploymentInfo.SEPPmailCloudDomain) {Write-Warning "Domain `"$SEPPmailCloudDomain`" does not fit to collected deployment info, just detected domain `"$($deploymentInfo.SEPPmailcloudDomain)`""} if ($routing -ne $deploymentInfo.routing) {Write-Error "Routing mode `"$routing`" does not fit to deployment info, just detected routing mode `"$($DeploymentInfo.routing)`" STOPPING because deployment will FAIL";break} if ($region -ne $deploymentInfo.region) {Write-Error "Region `"$region`" does not fit to deployment info, just detected region `"$($deploymentInfo.region)`" STOPPING because deployment will FAIL";break} Write-Verbose "Confirming if $SEPPmailCloudDomain is part or the tenant" $TenantDefaultDomain = $null foreach ($validationDomain in $SEPPmailCloudDomain) { if ((Confirm-SC365TenantDefaultDomain -ValidationDomain $validationDomain) -eq $true) { Write-verbose "Domain is part of the tenant and the Default Domain" $TenantDefaultDomain = $ValidationDomain } else { if ((Confirm-SC365TenantDefaultDomain -ValidationDomain $validationDomain) -eq $false) { Write-verbose "Domain is part of the tenant" } else { Write-Error "Domain is NOT Part of the tenant" break } } } } } Process { try { if ($force) { Remove-SC365Setup } } catch { throw [System.Exception] "Error: $($_.Exception.Message)" Write-Error "Setup removal failed. Try removing SEPPmail.cloud Rules and Connectors from the Microsoft portal admin.microsoft.com or with native Exchange Online PowerShell Module CmdLets." break } # For Connectors - use Tenant Default Domain # For TransportRules, use all domains in the array if ($SEPPmailCloudDomain.count -le 1) { $ConnectorDomain = $SEPPmailCloudDomain[0] } else { $ConnectorDomain = $TenantDefaultDomain } try { if ($InBoundOnly -eq $true) { Write-Information '--- Creating inbound connector ---' -InformationAction Continue New-SC365Connectors -SEPPmailCloudDomain $ConnectorDomain -routing $routing -region $region -inboundonly:$true } else { Write-Information '--- Creating in and outbound connectors ---' -InformationAction Continue New-SC365Connectors -SEPPmailCloudDomain $ConnectorDomain -routing $routing -region $region } } catch { throw [System.Exception] "Error: $($_.Exception.Message)" break } try { if ($inboundonly -eq $false) { Write-Information '--- Creating transport rules ---' -InformationAction Continue New-SC365Rules -SEPPmailCloudDomain $SEPPmailCloudDomain -routing $routing } } catch { throw [System.Exception] "Error: $($_.Exception.Message)" break } } End{ Write-Information "--- Successfully created SEPPmail.cloud Setup for $SEPPmailCloudDomain in region $region in $routing mode ---" -InformationAction Continue Write-Information "--- Wait a few minutes until changes are applied in the Microsoft cloud ---" -InformationAction Continue Write-Information "--- Afterwards, start testing E-Mails in and out ---" -InformationAction Continue } } <# .SYNOPSIS Reads all Rules and Connectors for SEPPmail.cloud in an Exo-Tenant .DESCRIPTION Based on autodiscovery, or forced values through parameters, Get-SC365Setup reads all connectors and rules from an Exo-Tenant .NOTES - none - .LINK https://github.com/seppmail/seppmail365cloud .EXAMPLE Get-SC365Setup # Without any parameters, it runs discovery mode and reads rules and connectors .EXAMPLE Get-SC365Setup -parallel # Reads parallel setup config .EXAMPLE Get-SC365Setup -inline # Reads inline setup config .EXAMPLE Get-SC365Setup -inline -inBoundOnly # Reads inline in "InbohndOnly" mode setup config #> function Get-SC365Setup { [CmdletBinding()] param() Begin { if(!(Test-SC365ConnectionStatus)) { throw [System.Exception] "You're not connected to Exchange Online - please connect prior to using this CmdLet" } else { Write-Verbose "Connected to Exchange Organization `"$Script:ExODefaultDomain`"" -InformationAction Continue } Write-Verbose "Detecting SEPPmail.cloud deployment scenario to read Exo Deployment" try { $deploymentInfo = Get-SC365DeploymentInfo } catch { Throw [System.Exception] "Could not autodetect SEPPmail.cloud Deployment Status, use manual parameters" } if ($deploymentInfo.DeployMentStatus -eq $false) { Write-Error "SEPPmail.cloud setup not (fully) deployed. Use Cloud-Portal and fix deployment." break } else { if ($deploymentInfo) { if ($deploymentInfo.Routing) {$Routing = $deploymentInfo.Routing} else {Write-Error "Cloud not autodetect routing info, use manual parameters"; break} if ($deploymentInfo.InBoundOnly -eq $true) {$InBoundOnly = $deploymentInfo.InBoundOnly} else {$InBoundOnly = $false} } } } Process { try { if ($InBoundOnly -eq $true) { Write-Verbose "Get SEPPmail.cloud Connectors in inbound-only mode" $smcConn = Get-SC365Connectors -Routing $routing -inboundOnly:$true } else { Write-Verbose "Get SEPPmail.cloud Connectors" $smcConn = Get-SC365Connectors -Routing $routing -inboundOnly:$false } if ($InBoundOnly -eq $false) { Write-Verbose "Get SEPPmail.cloud Transport Rules" $smcTRules = Get-SC365Rules } } catch { Write-Warning "Found no or incomplete setup, please check manually in EAC." break } } End{ Out-Host -InputObject $smcConn -Paging if ($InBoundOnly -eq $false) { Out-Host -InputObject $smcTRules -Paging } } } function Update-SC365Setup { [CmdletBinding( SupportsShouldProcess = $true ) ] param( # Specifies the name for the backup to be created during the update process. [Parameter( Mandatory = $false, HelpMessage = "Provide a custom name for the backup mail flow object during the update process." )] [string]$BackupName = 'SC-BKP', # Specifies the name for the backup to be created during the update process. [Parameter( Mandatory = $false, HelpMessage = "Prefix for connectors for temporary swapping rules traffic" )] [string]$TempPrefix = 'temp' ) begin { $existEAValue = $ErrorActionPreference $ErrorActionPreference = 'SilentlyContinue' } process { #region Infoblock Write-Host "+---------------------------------------------------------------------+" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| This script is a helper and provides basic steps to upgrade your |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| SEPPmail.cloud/Exchange integration. It covers only STANDARD Setups!|" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| The Script will: |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| 1.) Check if there are any orphaned rule or connector objects |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| 2.) Rename SEPPmail.cloud Transport rules to `$backupName |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| 3.) Create Connectors with Temp Name |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| 4.) Set (200) outbound transport rule to New-Connector |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| 5.) Rename SEPPmail.cloud Connectors to `$backupName |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| 6.) Attach old Transport rules to old Connector with BackupNam |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| ----------------- OLD SETUP STILL RUNNING ------------------ |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| 7.) Rename NEW Connectors to original names |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| 8.) Create new transport rules -PlacementPriority BOTTOM |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| If you have any: |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| - customizations to SEPPmail.cloud rules |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| - other corporate transport rules |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| - disclaimer Services integrated via rules |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| - or other special scenarios in your Exo-Tenant |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| you need to adapt/change/post-configure the outcome of this script! |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| DO NOT JUST FIRE IT UP AND HOPE THINGS ARE GOING TO WORK !!!!!! |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "+---------------------------------------------------------------------+" -ForegroundColor Magenta -BackgroundColor Black #endregion $response = Read-Host "I have read and understood the above warning (Type MURPHY if you agree)!" if ($response -eq 'MURPHY') { Write-Verbose '1 - Checking if there are existing Backup objects in the Exchange Tenant' $backupWildCard = '[' + $backupName + '*' Write-Verbose "1a - Checking if there are any orphaned backup objects" if (!((Get-InboundConnector -Identity $BackupWildcard -ea SilentlyContinue) -or (Get-OutboundConnector -Identity $backupWildCard -ea SilentlyContinue) -or (Get-TransportRule -Identity $backupWildCard -ea SilentlyContinue))) { #region 1a - Get-DeploymentInfo Write-Verbose "1b - Getting DeploymentInfo" $DeplInfo = Get-SC365DeploymentInfo #region 2 - rename existing rules to backup name Write-Verbose "2 - Rename existing SEPPmail.cloud rules" $oldTrpRls = Get-TransportRule -Identity '[SEPPmail.cloud]*' foreach ($rule in $oldTrpRls) { Set-TransportRule -Identity $rule.Name -Name ($rule.Name -replace 'SEPPmail.Cloud', $BackupName) @psBoundParameters } #endregion rename existing rules to backup name #region 3 - create new connectors with temp Name Write-Verbose "3 - Creating new connectors with temp name" if ($PSCmdlet.ShouldProcess('New connectors','create')){ $newConnectors = New-SC365connectors -SEPPmailCloudDomain $DeplInfo.SEPPmailCloudDomain -region $DeplInfo.region -routing $DeplInfo.routing -NamePrefix $tempPrefix @psBoundParameters } #endregion create new connectors with temp Name #region 4 - set outbound rule to new connector (Inline 200, parallel 1xx) Write-Verbose "4 - Set outbound rules to new connector" $oldObc = Get-OutboundConnector -Identity '[SEPPmail.cloud] Outbound-*' [string]$tempObcName = $TempPrefix + $($OldObc.Identity) $rulesToChange = Get-TransportRule -Identity '[*' | Where-Object {($null -ne $_.RouteMessageOutboundConnector) -and ($_.Name -like "*$backupName*")} foreach ($rule in $rulesToChange) { Set-TransportRule -Identity $($rule.Identity) -RouteMessageOutboundConnector $tempObcName @psBoundParameters } #endregion set outbound rule to new connector #region 5 - rename old connectors to backup Names Write-Verbose "5 - Rename existing SEPPmail.cloud Connectors to $backupName" Write-Verbose "5a - Rename existing SEPPmail.cloud Inbound Connector" $oldIbc = Get-InboundConnector -Identity '[SEPPmail.cloud] Inbound-*' Set-InboundConnector -Identity $($OldIbc.Identity) -Name ($($OldIbc.Identity) -replace 'SEPPmail.Cloud',$backupName) @PSBoundParameters Write-Verbose "5b - Rename existing SEPPmail.cloud Outbound Connector" $oldObc = Get-OutBoundConnector -Identity "[SEPPmail.cloud] OutBound-*" Set-OutBoundConnector -Identity $($oldObc.Identity) -Name ($oldObc.Identity -replace 'SEPPmail.Cloud',$backupName) @PSBoundParameters #endregion #region 6 - attach old outbound rules to old connector Write-Verbose "6 - Set outbound rule to old backup connector again" $bkpConnWildcard = "[" + $backupName + "]*" $bkpObc = Get-OutboundConnector -Identity $bkpConnWildcard foreach ($rule in $rulesToChange) { Set-TransportRule -Identity $($rule.Identity) -RouteMessageOutboundConnector $bkpObc @PSBoundParameters } #endregion #region 7 - rename new connectors to final Names Write-Verbose "7 - Rename existing $tempPrefix connectors to final name" $finalObCName = ($newConnectors|Where-Object Identity -like '*OutBound*').Identity -replace "^$([regex]::Escape($tempPrefix))", "" Set-OutboundConnector -Identity ($newConnectors|Where-Object Identity -like '*OutBound*').Identity -Name $finalObcName @PSBoundParameters $finalIbcName = ($newConnectors|Where-Object Identity -like '*Inbound*').Identity -replace "^$([regex]::Escape($tempPrefix))", "" Set-InBoundConnector -Identity ($newConnectors|Where-Object Identity -like '*InBound*').Identity -Name $finalIbcName @PSBoundParameters #endregion #region 8 - create New Transport rules Disabled Write-Verbose "8 - Creating new Transport Rules" New-SC365Rules -SEPPmailCloudDomain $DeplInfo.SEPPmailCloudDomain -routing $DeplInfo.routing -PlacementPriority Bottom @PSBoundParameters -Disabled #endregion #Region Infobblock Success Write-Host "+---------------------------------------------------------------------+" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| The CmdLet has finiehd and the OLD SETUP [SC-BPK] IS STILL ACTIVE |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| CUSTOMIZE your setup and ENABLE the new setup when done. |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "| After final tests you can DELETE the [SC-BKP] objects |" -ForegroundColor Magenta -BackgroundColor Black Write-Host "+---------------------------------------------------------------------+" -ForegroundColor Magenta -BackgroundColor Black } else { Write-Error "STOPPING - Found Existing Backup Objects - clean up the environment from $BackupName objects (rules and connectors) and TRY again" break } } else { Write-Host "Wise decision! Analyze your integration with New-SC365ExoReport and come back again if you are more familiar with the environment." -ForegroundColor Green -BackgroundColor DarkGray } } end { $ErrorActionPreference = $existEAValue } } Register-ArgumentCompleter -CommandName New-SC365Setup -ParameterName SEPPmailCloudDomain -ScriptBlock $paramDomSB # SIG # Begin signature block # MIIVzAYJKoZIhvcNAQcCoIIVvTCCFbkCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBxkMdSaFLJ0YQz # dMq0zOn69Y/XZKlfPYqI3nDaJtiq8KCCEggwggVvMIIEV6ADAgECAhBI/JO0YFWU # jTanyYqJ1pQWMA0GCSqGSIb3DQEBDAUAMHsxCzAJBgNVBAYTAkdCMRswGQYDVQQI # DBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoM # EUNvbW9kbyBDQSBMaW1pdGVkMSEwHwYDVQQDDBhBQUEgQ2VydGlmaWNhdGUgU2Vy # dmljZXMwHhcNMjEwNTI1MDAwMDAwWhcNMjgxMjMxMjM1OTU5WjBWMQswCQYDVQQG # EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRTZWN0aWdv # IFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQCN55QSIgQkdC7/FiMCkoq2rjaFrEfUI5ErPtx94jGgUW+s # hJHjUoq14pbe0IdjJImK/+8Skzt9u7aKvb0Ffyeba2XTpQxpsbxJOZrxbW6q5KCD # J9qaDStQ6Utbs7hkNqR+Sj2pcaths3OzPAsM79szV+W+NDfjlxtd/R8SPYIDdub7 # P2bSlDFp+m2zNKzBenjcklDyZMeqLQSrw2rq4C+np9xu1+j/2iGrQL+57g2extme # me/G3h+pDHazJyCh1rr9gOcB0u/rgimVcI3/uxXP/tEPNqIuTzKQdEZrRzUTdwUz # T2MuuC3hv2WnBGsY2HH6zAjybYmZELGt2z4s5KoYsMYHAXVn3m3pY2MeNn9pib6q # RT5uWl+PoVvLnTCGMOgDs0DGDQ84zWeoU4j6uDBl+m/H5x2xg3RpPqzEaDux5mcz # mrYI4IAFSEDu9oJkRqj1c7AGlfJsZZ+/VVscnFcax3hGfHCqlBuCF6yH6bbJDoEc # QNYWFyn8XJwYK+pF9e+91WdPKF4F7pBMeufG9ND8+s0+MkYTIDaKBOq3qgdGnA2T # OglmmVhcKaO5DKYwODzQRjY1fJy67sPV+Qp2+n4FG0DKkjXp1XrRtX8ArqmQqsV/ # AZwQsRb8zG4Y3G9i/qZQp7h7uJ0VP/4gDHXIIloTlRmQAOka1cKG8eOO7F/05QID # AQABo4IBEjCCAQ4wHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYD # VR0OBBYEFDLrkpr/NZZILyhAQnAgNpFcF4XmMA4GA1UdDwEB/wQEAwIBhjAPBgNV # HRMBAf8EBTADAQH/MBMGA1UdJQQMMAoGCCsGAQUFBwMDMBsGA1UdIAQUMBIwBgYE # VR0gADAIBgZngQwBBAEwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21v # ZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEE # KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZI # hvcNAQEMBQADggEBABK/oe+LdJqYRLhpRrWrJAoMpIpnuDqBv0WKfVIHqI0fTiGF # OaNrXi0ghr8QuK55O1PNtPvYRL4G2VxjZ9RAFodEhnIq1jIV9RKDwvnhXRFAZ/ZC # J3LFI+ICOBpMIOLbAffNRk8monxmwFE2tokCVMf8WPtsAO7+mKYulaEMUykfb9gZ # pk+e96wJ6l2CxouvgKe9gUhShDHaMuwV5KZMPWw5c9QLhTkg4IUaaOGnSDip0TYl # d8GNGRbFiExmfS9jzpjoad+sPKhdnckcW67Y8y90z7h+9teDnRGWYpquRRPaf9xH # +9/DUp/mBlXpnYzyOmJRvOwkDynUWICE5EV7WtgwggYaMIIEAqADAgECAhBiHW0M # UgGeO5B5FSCJIRwKMA0GCSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYD # VQQKEw9TZWN0aWdvIExpbWl0ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENv # ZGUgU2lnbmluZyBSb290IFI0NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5 # NTlaMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzAp # BgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwggGiMA0G # CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCbK51T+jU/jmAGQ2rAz/V/9shTUxjI # ztNsfvxYB5UXeWUzCxEeAEZGbEN4QMgCsJLZUKhWThj/yPqy0iSZhXkZ6Pg2A2NV # DgFigOMYzB2OKhdqfWGVoYW3haT29PSTahYkwmMv0b/83nbeECbiMXhSOtbam+/3 # 6F09fy1tsB8je/RV0mIk8XL/tfCK6cPuYHE215wzrK0h1SWHTxPbPuYkRdkP05Zw # mRmTnAO5/arnY83jeNzhP06ShdnRqtZlV59+8yv+KIhE5ILMqgOZYAENHNX9SJDm # +qxp4VqpB3MV/h53yl41aHU5pledi9lCBbH9JeIkNFICiVHNkRmq4TpxtwfvjsUe # dyz8rNyfQJy/aOs5b4s+ac7IH60B+Ja7TVM+EKv1WuTGwcLmoU3FpOFMbmPj8pz4 # 4MPZ1f9+YEQIQty/NQd/2yGgW+ufflcZ/ZE9o1M7a5Jnqf2i2/uMSWymR8r2oQBM # dlyh2n5HirY4jKnFH/9gRvd+QOfdRrJZb1sCAwEAAaOCAWQwggFgMB8GA1UdIwQY # MBaAFDLrkpr/NZZILyhAQnAgNpFcF4XmMB0GA1UdDgQWBBQPKssghyi47G9IritU # pimqF6TNDDAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNV # HSUEDDAKBggrBgEFBQcDAzAbBgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEsG # A1UdHwREMEIwQKA+oDyGOmh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1 # YmxpY0NvZGVTaWduaW5nUm9vdFI0Ni5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsG # AQUFBzAChjpodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2Rl # U2lnbmluZ1Jvb3RSNDYucDdjMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0 # aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEABv+C4XdjNm57oRUgmxP/BP6YdURh # w1aVcdGRP4Wh60BAscjW4HL9hcpkOTz5jUug2oeunbYAowbFC2AKK+cMcXIBD0Zd # OaWTsyNyBBsMLHqafvIhrCymlaS98+QpoBCyKppP0OcxYEdU0hpsaqBBIZOtBajj # cw5+w/KeFvPYfLF/ldYpmlG+vd0xqlqd099iChnyIMvY5HexjO2AmtsbpVn0OhNc # WbWDRF/3sBp6fWXhz7DcML4iTAWS+MVXeNLj1lJziVKEoroGs9Mlizg0bUMbOalO # hOfCipnx8CaLZeVme5yELg09Jlo8BMe80jO37PU8ejfkP9/uPak7VLwELKxAMcJs # zkyeiaerlphwoKx1uHRzNyE6bxuSKcutisqmKL5OTunAvtONEoteSiabkPVSZ2z7 # 6mKnzAfZxCl/3dq3dUNw4rg3sTCggkHSRqTqlLMS7gjrhTqBmzu1L90Y1KWN/Y5J # KdGvspbOrTfOXyXvmPL6E52z1NZJ6ctuMFBQZH3pwWvqURR8AgQdULUvrxjUYbHH # j95Ejza63zdrEcxWLDX6xWls/GDnVNueKjWUH3fTv1Y8Wdho698YADR7TNx8X8z2 # Bev6SivBBOHY+uqiirZtg0y9ShQoPzmCcn63Syatatvx157YK9hlcPmVoa1oDE5/ # L9Uo2bC5a4CH2RwwggZzMIIE26ADAgECAhAMcJlHeeRMvJV4PjhvyrrbMA0GCSqG # SIb3DQEBDAUAMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0 # ZWQxKzApBgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYw # HhcNMjMwMzIwMDAwMDAwWhcNMjYwMzE5MjM1OTU5WjBqMQswCQYDVQQGEwJERTEP # MA0GA1UECAwGQmF5ZXJuMSQwIgYDVQQKDBtTRVBQbWFpbCAtIERldXRzY2hsYW5k # IEdtYkgxJDAiBgNVBAMMG1NFUFBtYWlsIC0gRGV1dHNjaGxhbmQgR21iSDCCAiIw # DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOapobQkNYCMP+Y33JcGo90Soe9Y # /WWojr4bKHbLNBzKqZ6cku2uCxhMF1Ln6xuI4ATdZvm4O7GqvplG9nF1ad5t2Lus # 5SLs45AYnODP4aqPbPU/2NGDRpfnceF+XhKeiYBwoIwrPZ04b8bfTpckj/tvenB9 # P8/9hAjWK97xv7+qsIz4lMMaCuWZgi8RlP6XVxsb+jYrHGA1UdHZEpunEFLaO9Ss # OPqatPAL2LNGs/JVuGdq9p47GKzn+vl+ANd5zZ/TIP1ifX76vorqZ9l9a5mzi/HG # vq43v2Cj3jrzIQ7uTbxtiLlPQUqkRzPRtiwTV80JdtRE+M+gTf7bT1CTvG2L3scf # YKFk7S80M7NydxV/qL+l8blGGageCzJ8svju2Mo4BB+ALWr+gBmCGqrM8YKy/wXR # tbvdEvBOLsATcHX0maw9xRCDRle2jO+ndYkTKZ92AMH6a/WdDfL0HrAWloWWSg62 # TxmJ/QiX54ILQv2Tlh1Al+pjGHN2evxS8i+XoWcUdHPIOoQd37yjnMjCN593wDzj # XCEuDABYw9BbvfSp29G/uiDGtjttDXzeMRdVCJFgULV9suBVP7yFh9pK/mVpz+aC # L2PvqiGYR41xRBKqwrfJEdoluRsqDy6KD985EdXkTvdIFKv0B7MfbcBCiGUBcm1r # fLAbs8Q2lqvqM4bxAgMBAAGjggGpMIIBpTAfBgNVHSMEGDAWgBQPKssghyi47G9I # ritUpimqF6TNDDAdBgNVHQ4EFgQUL96+KAGrvUgJnXwdVnA/uy+RlEcwDgYDVR0P # AQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwSgYD # VR0gBEMwQTA1BgwrBgEEAbIxAQIBAwIwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9z # ZWN0aWdvLmNvbS9DUFMwCAYGZ4EMAQQBMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHA6 # Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nQ0FSMzYu # Y3JsMHkGCCsGAQUFBwEBBG0wazBEBggrBgEFBQcwAoY4aHR0cDovL2NydC5zZWN0 # aWdvLmNvbS9TZWN0aWdvUHVibGljQ29kZVNpZ25pbmdDQVIzNi5jcnQwIwYIKwYB # BQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29tMB4GA1UdEQQXMBWBE3N1cHBv # cnRAc2VwcG1haWwuY2gwDQYJKoZIhvcNAQEMBQADggGBAHnWpS4Jw/QiiLQi2EYv # THCtwKsj7O3G7wAN7wijSJcWF7iCx6AoCuCIgGdWiQuEZcv9pIUrXQ6jOSRHsDNX # SvIhCK9JakZJSseW/SCb1rvxZ4d0n2jm2SdkWf5j7+W+X4JHeCF9ZOw0ULpe5pFs # IGTh8bmTtUr3yA11yw4vHfXFwin7WbEoTLVKiL0ZUN0Qk+yBniPPSRRlUZIX8P4e # iXuw7lh9CMaS3HWRKkK89w//18PjUMxhTZJ6dszN2TAfwu1zxdG/RQqvxXUTTAxU # JrrCuvowtnDQ55yXMxkkSxWUwLxk76WvXwmohRdsavsGJJ9+yxj5JKOd+HIZ1fZ7 # oi0VhyOqFQAnjNbwR/TqPjRxZKjCNLXSM5YSMZKAhqrJssGLINZ2qDK/CEcVDkBS # 6Hke4jWMczny8nB8+ATJ84MB7tfSoXE7R0FMs1dinuvjVWIyg6klHigpeEiAaSaG # 5KF7vk+OlquA+x4ohPuWdtFxobOT2OgHQnK4bJitb9aDazGCAxowggMWAgEBMGgw # VDELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDErMCkGA1UE # AxMiU2VjdGlnbyBQdWJsaWMgQ29kZSBTaWduaW5nIENBIFIzNgIQDHCZR3nkTLyV # eD44b8q62zANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgACh # AoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAM # BgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCCa+QWZsQInnMPCdQJhBe1e2CdZ # gi6CUhSIRaJEQx1C5zANBgkqhkiG9w0BAQEFAASCAgCZHdQsqffd1LFnDRTDaXhq # 6bEaYiNPhL3dT82BVhgmt0iNI+5RaxObKXiCKKeFRJqrYLM5rY5z2HW/ri1YBKLI # oJ73u7TlPxH6FcctaK+N1UEYcBsA89PK1CGZtCFma7icuuuDPAeaRImnsQKHXN39 # wms9EqTmWtrbfKJhz8X3Q7trfb+l9dCrApZwRegWc4YtYSwThzKtjsylYxhZ7Get # 7+JHqFd9LrEx2K11C6jpL4yj66W2b0XzVeQrBBV0Sv7iqTAw9A8uvJDTODrdVjkC # Q3HTi48kNYSHtehLHxK7tg9dmULiFtLhBqIdd41EeTxN5AzOmK0HwxcFuG9vdEeI # BPkXdlmA/mL+5xRWc6anKp7zxfapXEVziTPYfEyg4LvngfHe9W8GnmfGR89+S+/v # 4Fm6o9yhcgCW9bGXncfaYraXMgE34AzAcdpA5VuXVoPEnaUu1h/9rzj+ZRvrtrtN # Y5JgVwwMIb5dKiFTboj9erXAJ5ePgNbWa37tzAX0zHgQjgQ5MGL2MBxSOovp4vL0 # UseVjfhOhpXkDN+NKjuehf4kInk0P6Wgm43z8Xw0L87W9ufVF+2Bef4+DOEy2779 # 0rssc0hS1Jx7p5p6HxogsJMvJOv7NDEaZx/BGR2RzKTYC2Syc1eve4YnxVNJ8Egi # e/PikuxgrTlSTbKZ47zcEw== # SIG # End signature block |