Remediation/Consolidation/CON-ALIASES.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 Prepares the prefix of the aliases that collide with the target or another source (procedure CON-ALIASES). .DESCRIPTION Starts from the colliding aliases in the plan and finds the objects in each source. - Security groups with no mail: set aside, their alias need not be unique. - Generic mailboxes (shared, rooms): set aside, their fate is decided in CON-SHARED-MAILBOXES. - Accounts marked Duplicate in the draft: set aside, no object is created in the target. decisions\CON-ALIASES.csv holds the rest: Prefix or Do not migrate. The Target alias column can be edited, and stays from one preview to the next. For an account, the alias comes from Temporary UPN; for a group, it gets the prefix of the source. The script changes neither the matrix nor any setting. IDENTITY-MATRIX takes the decided aliases of accounts into Temporary UPN; the decided aliases of groups go in the copy plan. The check asks Exchange Online of the target (Get-Recipient): each chosen alias must be free there. Do not rename an existing object of the target without a customer decision. #> $c = Open-TcRemediation $DecisionName = 'CON-ALIASES' $DecisionKey = @('Tenant', 'Alias', 'Object') $GenericTypes = @('SharedMailbox', 'RoomMailbox', 'EquipmentMailbox', 'SchedulingMailbox') $entries = Read-TcDecisionEntry $DecisionName $DecisionKey $collisions = @((Get-TcMember (Get-TcRemediationPlanAction -Procedure 'CON-ALIASES') 'items') | ForEach-Object { (([string]$_ -split ': ')[0]).Trim().ToLowerInvariant() } | Where-Object { $_ } | Select-Object -Unique) $targetAlias = @{} foreach ($table in 'mailboxes', 'groups', 'microsoft365Groups', 'distributionGroups') { foreach ($x in Get-TcRemediationTable 'Target' $table) { $a = ([string]$x['Alias']).ToLowerInvariant(); if ($a) { $targetAlias[$a] = 1 } } } $proposals = [System.Collections.Generic.List[object]]::new() $chosenRows = @{} foreach ($t in $c.Tenants) { Write-TcRemediationTenant $t 'Colliding aliases' if ($t -eq 'Target') { Write-TcRemediationNote ("{0} colliding aliases in the plan, {1} aliases already taken in the target" -f $collisions.Count, $targetAlias.Count); continue } $i = Get-TcRemediationTenant $t $prefix = ((Get-TcText $i 'Prefix')).ToLowerInvariant() $draft = @{} foreach ($l in Import-TcMatrixDraft $t) { $draft[$l.'Source UPN'.ToLowerInvariant()] = $l } $n = @{ NoMail = 0; GenericItems = 0; Duplicates = 0; Rows = 0 } foreach ($b in Get-TcRemediationTable $t 'mailboxes' | Where-Object { ([string]$_['Alias']).ToLowerInvariant() -in $collisions }) { if ($b['Type'] -in $GenericTypes) { $n.GenericItems++; continue } $l = $draft[([string]$b['UPN']).ToLowerInvariant()] if (-not $l -or -not (Get-TcMember $l 'Temporary UPN')) { $n.Duplicates++; continue } $proposals.Add([ordered]@{ Tenant = $t; Alias = $b['Alias']; Object = $b['UPN']; 'Object type' = 'Account'; 'Target alias' = (Get-TcAddressPrefix (Get-TcMember $l 'Temporary UPN')); Free = ''; Proposal = 'Prefix' }) $n.Rows++ } foreach ($g in Get-TcRemediationTable $t 'groups' | Where-Object { ([string]$_['Alias']).ToLowerInvariant() -in $collisions }) { if (-not $g['Email']) { $n.NoMail++; continue } $proposals.Add([ordered]@{ Tenant = $t; Alias = $g['Alias']; Object = $g['Email']; 'Object type' = "Group: $($g['Type'])"; 'Target alias' = ('{0}-{1}' -f $prefix, ([string]$g['Alias']).ToLowerInvariant()); Free = ''; Proposal = 'Prefix' }) $n.Rows++ } Write-TcRemediationNote ("{0} object(s) to settle; set aside: {1} generic mailbox(es) (CON-SHARED-MAILBOXES), {2} duplicate(s), {3} group(s) without mail" -f (Get-TcMember $n 'Rows'), (Get-TcMember $n 'GenericItems'), (Get-TcMember $n 'Duplicates'), (Get-TcMember $n 'NoMail')) foreach ($g in @(Get-TcSourceGroup -Tenant $t)) { $alias = ((Get-TcText $g 'name') -replace '[^A-Za-z0-9\-]', '').ToLowerInvariant() if (-not $alias.StartsWith("$prefix-")) { Write-TcRemediationAlert ("{0}: the group {1} of the configuration is not prefixed" -f $t, (Get-TcMember $g 'name')) } if ($targetAlias[$alias]) { Write-TcRemediationAlert ("{0}: the alias {1}, taken from the group {2}, already exists in the target" -f $t, $alias, (Get-TcMember $g 'name')) } } } foreach ($p in $proposals) { $s = $entries[(Get-TcDecisionKey ([pscustomobject]$p) $DecisionKey)] if ($s -and (Get-TcMember $s 'Target alias')) { $p['Target alias'] = (Get-TcMember $s 'Target alias') } $a = ([string]$p['Target alias']).ToLowerInvariant() $p.Free = $(if ($a -and -not $targetAlias[$a] -and -not $chosenRows[$a]) { 'Yes' } else { 'No' }) if ($a) { $chosenRows[$a] = 1 } if ((Get-TcMember $p 'Free') -eq 'No') { Write-TcRemediationAlert ("{0}: the target alias {1} is not free" -f (Get-TcMember $p 'Tenant'), $p['Target alias']) } } $decisions = @(Get-TcDecision -Name $DecisionName -Proposal @($proposals) -Key $DecisionKey -Choice 'Prefix', 'Do not migrate') foreach ($t in $c.Tenants | Where-Object { $_ -ne 'Target' }) { $groups = @($decisions | Where-Object { (Get-TcMember $_ 'Tenant') -eq $t -and (Get-TcMember $_ 'Decision') -eq 'Prefix' -and (Get-TcMember $_ 'Object type') -like 'Group*' }) if (-not $groups.Count) { continue } $f = Export-TcRemediationReport -Name "group-aliases-$t" -Rows $groups Add-TcManualAction -Tenant 'Target' -Object "Group aliases ($t)" -Description ("Record {0} group alias(es) of {1} in the copy plan" -f $groups.Count, $t) ` -Instruction @("List: $f", 'The alias of each copied group must be the one in the Target alias column.') } Invoke-TcRemediationPlan -Verify { param($t) if ($t -ne 'Target') { return } $takenOnes = @($decisions | Where-Object { (Get-TcMember $_ 'Decision') -eq 'Prefix' -and (Get-TcMember $_ 'Target alias') } | Where-Object { @(Invoke-TcServiceCommand -Tenant $t -Service Exo -Command 'Get-Recipient' -Parameters @{ Filter = ("Alias -eq '{0}'" -f (Get-TcMember $_ 'Target alias').Replace("'", "''")) }).Count }) if ($takenOnes.Count) { New-TcRemediationResult Blocking ("{0} aliases already taken in the target: {1}" -f $takenOnes.Count, (@((Get-TcMember $takenOnes 'Target alias')) -join ', ')) } else { New-TcRemediationResult OK 'all chosen aliases are free in the target' } } Close-TcRemediation |