Remediation/Consolidation/CON-SETTINGS.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
    Has the single rule of the target chosen for each setting set differently from one source to the other (procedure CON-SETTINGS).
 
.DESCRIPTION
    Data read: the gaps of the plan, with the value of the target and of each source.
    decisions\CON-SETTINGS.csv: one row per setting. The proposal keeps the value of the target.
      - Keep target: nothing changes in the target;
      - Change target: enter the value in the Chosen value column. A rule looser than the target design weakens the whole target: it
        needs a written decision of the customer.
 
    This script does not touch SharePoint or Teams: one process cannot load both their modules. The writes are in CON-SETTINGS-SPO and
    CON-SETTINGS-TEAMS, which read this file. The other settings are declared as work for a person, because their own procedure holds
    them (methods: EMERGENCY-ACCESS and ENT-MFA-REGISTRATION; invitations: ENT-AUTHORIZATION). The report says which source loses a
    capability, for the communication.
#>

[void](Open-TcRemediation)

$DecisionFile = 'CON-SETTINGS'
$Keys = @('Service', 'Setting')
$SourceNames = @{}; foreach ($role in (Get-TcSourceRole)) { $SourceNames[(Get-TcRemediationTenant $role)['Name']] = $role }

function Get-ItemValue {
    <# A value list in the form role value, role value becomes a dictionary; a value can be empty. #>
    param([string]$Text)
    $v = @{}; $target = (Get-TcRemediationTenant 'Target')['Name']
    foreach ($p in ($Text -split ' - ')) {
        foreach ($n in @($target) + @($SourceNames.Keys)) { if ($p -eq $n -or $p.StartsWith("$n ") -or $p.StartsWith("$n`:")) { $v[$(if ($n -eq $target) { 'Target' } else { $SourceNames[$n] })] = ($p.Substring((Get-TcMember $n 'Length')) -replace '^:', '').Trim() } }
    }
    return $v
}

$existingItems = Read-TcDecisionEntry -Name $DecisionFile -Key $Keys
Write-TcRemediationTenant 'Target' 'Settings set differently'
$items = @(Get-TcRemediationPlanAction -Procedure 'CON-SETTINGS' | ForEach-Object { (Get-TcMember $_ 'items') } | Where-Object { $_ })
Write-TcRemediationNote ("{0} setting(s) differ from one source to the other" -f $items.Count)

$proposals = foreach ($e in $items) {
    if ([string]$e -notmatch '^(?<s>.+?) - (?<p>.+?): (?<v>.*)$') { Write-TcRemediationAlert "Unreadable plan item: $e"; continue }
    $service = $Matches['s']; $setting = $Matches['p']
    $v = Get-ItemValue $Matches['v']
    $l = [ordered]@{ Service = $service; Setting = $setting; Target = $v['Target'] }; foreach ($role in (Get-TcSourceRole)) { $l[$role] = $v[$role] }; $l['Chosen value'] = ''; $l['Proposal'] = 'Keep target'
    $before = $existingItems[(Get-TcDecisionKey ([pscustomobject]$l) $Keys)]
    if ($before) { $l.'Chosen value' = (Get-TcMember $before 'Chosen value') }
    $l
}

$decisions = @(Get-TcDecision -Name $DecisionFile -Proposal @($proposals | Where-Object { $_ }) -Key $Keys -Choice 'Keep target', 'Change target')

foreach ($d in $decisions | Where-Object { (Get-TcMember $_ 'Decision') -eq 'Change target' -and -not (Get-TcMember $_ 'Chosen value') }) { Write-TcRemediationAlert ("{0}: decision Change target with no chosen value" -f (Get-TcMember $d 'Setting')) }
foreach ($d in $decisions | Where-Object { (Get-TcMember $_ 'Decision') -eq 'Change target' -and (Get-TcMember $_ 'Chosen value') -and (Get-TcMember $_ 'Service') -notmatch '^(SharePoint|Teams)' }) {
    Add-TcManualAction -Tenant 'Target' -Object (Get-TcMember $d 'Setting') -Description ("Apply the chosen rule for {0}: {1}" -f (Get-TcMember $d 'Setting'), (Get-TcMember $d 'Chosen value')) -Who 'Team' `
        -Instruction @(("{0}, {1}: chosen value {2}." -f (Get-TcMember $d 'Service'), (Get-TcMember $d 'Setting'), (Get-TcMember $d 'Chosen value')), 'This setting belongs to another procedure or portal: apply it there, then check at the next assessment.')
}

$losing = foreach ($d in $decisions) {
    $rule = if ((Get-TcMember $d 'Decision') -eq 'Change target') { (Get-TcMember $d 'Chosen value') } else { (Get-TcMember $d 'Target') }
    foreach ($n in $SourceNames.Keys) {
        if ((Get-TcText $d $n) -ne [string]$rule) { [ordered]@{ Source = $n; Service = (Get-TcMember $d 'Service'); Setting = (Get-TcMember $d 'Setting'); 'Today' = (Get-TcMember $d $n); 'Chosen rule' = $rule } }
    }
}
if (@($losing | Where-Object { $_ }).Count) { Export-TcRemediationReport -Name 'communication-by-unit' -Rows @($losing | Where-Object { $_ }) | Out-Null }

Invoke-TcRemediationPlan
Close-TcRemediation