Remediation/DomainsAndDNS/DNS-SKYPE.ps1
|
# UNTESTED: this script has never been run on a real tenant. Read it, try it on a test tenant first, and use it at your own risk. <# .SYNOPSIS Handles the legacy Skype and Teams records of the migrated domains (procedure DNS-SKYPE). .DESCRIPTION Decision: decisions\DNS-SKYPE.csv, one row per record read in publicDns. Remove or Keep. The federation SRV is proposed as Keep: removing it can cut the external conversations of the domain. Run it after the domains are verified in the target: the expected values are read there. In an -Apply run, while the domain is not verified there, the script does nothing and says so. A record the target expects stays in place, whatever the decision. The cutover publishes the Email service only. Add-TcDnsAction does not handle SRV: their removal goes through an instruction, and the script watches the public DNS. The test of an external conversation stays to be done by a person. #> $c = Open-TcRemediation $Reason = '^(lyncdiscover|sip)\.|^_sip\._tls\.|^_sipfederationtls\._tcp\.' $TokenVariable = [string](Get-TcRemediationOption 'DnsApiTokenVariable') $Tracking = @{} function Test-SrvMissing { param([string]$Name) try { return -not @(Resolve-TcDnsTransport -Name $Name -Type SRV -Server (Get-TcRemediationOption 'DnsServer') | Where-Object { (Get-TcMember $_ 'Type') -eq 'SRV' }).Count } catch { return $true } } $proposals = foreach ($t in $c.Tenants) { Write-TcRemediationTenant $t $readItems = @(Get-TcRemediationTable $t 'publicDns' | Where-Object { $_['Record'] -match $Reason }) Write-TcRemediationNote ("{0} legacy record(s) read" -f $readItems.Count) foreach ($e in $readItems) { [ordered]@{ Tenant = $t; Domain = $e['Domain']; Record = $e['Record']; Type = $e['Type']; Value = $e['Value'] Proposal = $(if ($e['Record'] -like '_sipfederationtls*') { 'Keep' } else { 'Remove' }) } } } $decisions = @(Get-TcDecision -Name 'DNS-SKYPE' -Proposal @($proposals) -Key 'Tenant', 'Record' -Choice 'Remove', 'Keep') foreach ($t in $c.Tenants) { foreach ($d in (Get-TcMember (Get-TcRemediationTenant $t) 'Domains')) { $removals = @($decisions | Where-Object { (Get-TcMember $_ 'Tenant') -eq $t -and (Get-TcMember $_ 'Domain') -eq $d -and (Get-TcMember $_ 'Decision') -eq 'Remove' }) $h = Get-TcDnsHost $d $expectedItems = @() if ($c.Apply) { $domain = @(Invoke-TcGraph -Tenant 'Target' -Uri 'v1.0/domains' -All) | Where-Object { (Get-TcMember $_ 'id') -eq $d } | Select-Object -First 1 if (-not $domain -or -not (Get-TcMember $domain 'isVerified')) { Write-TcRemediationAlert "$d is not verified in the target: run again after the domains are verified in the target"; continue } $expectedItems = @(Invoke-TcGraph -Tenant 'Target' -Uri "v1.0/domains/$d/serviceConfigurationRecords" -All | Where-Object { (Get-TcMember $_ 'supportedService') -eq 'OfficeCommunicationsOnline' } | ForEach-Object { ((Get-TcText $_ 'label')).ToLowerInvariant() }) } foreach ($r in $removals) { $name = (Get-TcText $r 'Record') if ($name.ToLowerInvariant() -in $expectedItems) { Write-TcRemediationAlert "${name}: the target expects this record, Remove decision ignored"; continue } if ((Get-TcMember $r 'Type') -eq 'CNAME') { if ($h -eq 'Cloudflare' -and $c.Apply -and -not [Environment]::GetEnvironmentVariable($TokenVariable)) { Write-TcRemediationAlert ("{0}: Cloudflare token missing (variable {1}), skipped" -f $name, $TokenVariable); continue } Add-TcDnsAction -Tenant $t -Zone $d -Name $name -Type CNAME -Value ((Get-TcText $r 'Value')) -Action Remove -Description "Remove the legacy CNAME $name" ` -Test { param($a) -not @(Resolve-TcDnsEntry -Name $a.dns.name -Type CNAME).Count } } else { $object = "$((Get-TcMember $r 'Type')) $name" $Tracking[$object] = $name Add-TcManualAction -Tenant $t -Object $object -Description "Remove the legacy record $object" -Minutes 120 -IntervalSeconds 120 -Instruction @( "To do at $h, zone ${d}: remove the $((Get-TcMember $r 'Type')) record $name (value $((Get-TcMember $r 'Value'))).", 'The script watches public DNS and continues on its own.') -WaitUntil { param($a) Test-SrvMissing -Name $Tracking[$a.object] } } } if (@($decisions | Where-Object { (Get-TcMember $_ 'Tenant') -eq $t -and (Get-TcMember $_ 'Domain') -eq $d }).Count) { Add-TcManualAction -Tenant $t -Object "External conversation $d" -Description "Test an external Teams chat from an account of $d" -Instruction @("After the cutover, open a Teams chat with a contact from another organization from an account of $d, in both directions.") } } } Invoke-TcRemediationPlan -Verify { param($t) foreach ($d in (Get-TcMember (Get-TcRemediationTenant $t) 'Domains')) { $name = "_sipfederationtls._tcp.$d" $srv = try { @(Resolve-TcDnsTransport -Name $name -Type SRV -Server (Get-TcRemediationOption 'DnsServer') | Where-Object { (Get-TcMember $_ 'Type') -eq 'SRV' }) } catch { @() } if (-not @($decisions | Where-Object { (Get-TcMember $_ 'Tenant') -eq $t -and (Get-TcMember $_ 'Domain') -eq $d }).Count) { continue } if (-not (Get-TcCount $srv)) { New-TcRemediationResult Warning "${name}: missing, external exchanges may be cut off" } elseif ((Get-TcText $srv[0] 'NameTarget') -match 'sipfed\.online\.lync\.com' -and [int](Get-TcMember $srv[0] 'Port') -eq 5061) { New-TcRemediationResult OK "${name}: as expected" } else { New-TcRemediationResult Warning ("{0}: {1}:{2}, sipfed.online.lync.com:5061 attendu" -f $name, (Get-TcMember $srv[0] 'NameTarget'), (Get-TcMember $srv[0] 'Port')) } } } Close-TcRemediation |