Public/Install-NSPConditionalAccessCoverageTask.ps1
|
function Install-NSPConditionalAccessCoverageTask { <# .SYNOPSIS Opts a tenant into scheduled coverage and registers the task that runs it. Plan-only by default. .DESCRIPTION Without -Execute, reports what it would do and whether the prerequisites are in place: the coverage app is registered on this host (Register-NSPConditionalAccessCoverageApp) when the Cloud side runs, and the ActiveDirectory module is present when the AD side runs. Without the module, AD adds go to a script and the run reports OperatorActionRequired. With -Execute and confirmation: - sets Coverage.Scheduled in the tenant profile (Enabled, Sides, MaxAdds, TaskName); - registers the task: Windows PowerShell runs Invoke-NSPConditionalAccessCoverageJob every -EveryHours hours from -At, as -RunAs (default SYSTEM, which needs the certificate in Cert:\LocalMachine\My, and Microsoft.Graph.Authentication installed for all users). A Held or Failed run exits non-zero. Stop it with Set-NSPConditionalAccessCoverageSetting -ScheduledEnabled $false: the next run does nothing and reports Disabled. Unregister-ScheduledTask removes the task. .EXAMPLE Install-NSPConditionalAccessCoverageTask -TenantProfile $profilePath .EXAMPLE Install-NSPConditionalAccessCoverageTask -TenantProfile $profilePath -EveryHours 4 -MaxAdds 10 -Execute #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param( [Parameter(Mandatory)][string]$TenantProfile, [ValidateSet('Cloud', 'ActiveDirectory')][string[]]$Side, [ValidateRange(1, 1000)][int]$MaxAdds = 10, [ValidateRange(1, 24)][int]$EveryHours = 4, [datetime]$At = (Get-Date).Date.AddDays(1).AddHours(6), [string]$RunAs = 'SYSTEM', [switch]$Execute, [string]$ConfigRoot ) $profilePath = (Resolve-Path -LiteralPath $TenantProfile).Path $content = Get-Content -LiteralPath $profilePath -Raw | ConvertFrom-Json $tenantId = $content.TenantId if (-not $Side) { $Side = if ($content.Hybrid) { @('Cloud', 'ActiveDirectory') } else { @('Cloud') } } $settings = Get-NSPConditionalAccessCoverageSetting -TenantProfile $content if ($MaxAdds -gt $settings.MaxAdds) { throw "The scheduled cap ($MaxAdds) cannot exceed the profile's MaxAdds ($($settings.MaxAdds))." } $taskName = "NSP Conditional Access coverage - $(if ($content.TenantDomain) { $content.TenantDomain } else { $tenantId })" $modulePath = Join-Path $script:ModuleRoot 'NSP.M365.ConditionalAccess.psd1' # The task account would resolve its own default root (another user's LOCALAPPDATA), so the # job is always given this session's. if (-not $ConfigRoot) { $ConfigRoot = Get-NSPConditionalAccessStateRoot } $jobCommand = "Import-Module '$($modulePath -replace "'", "''")' -ErrorAction Stop; " + "`$r = Invoke-NSPConditionalAccessCoverageJob -TenantProfile '$($profilePath -replace "'", "''")' -ConfigRoot '$($ConfigRoot -replace "'", "''")'; " + "if (`$r.Status -in 'Held', 'Failed') { exit 1 }" $arguments = "-NoProfile -NonInteractive -ExecutionPolicy Bypass -Command `"$($jobCommand -replace '"', '\"')`"" $problems = [System.Collections.Generic.List[string]]::new() if ($Side -contains 'Cloud' -and -not (Test-Path -LiteralPath (Join-Path (Get-NSPConditionalAccessLocalPath -Kind CoverageApps -ConfigRoot $ConfigRoot) "$tenantId.json"))) { $problems.Add('No coverage app is registered on this host. Run Register-NSPConditionalAccessCoverageApp -Execute first.') } $notes = @(if ($Side -contains 'ActiveDirectory' -and -not (Test-NSPConditionalAccessAdModule)) { 'This host has no ActiveDirectory module: on-premises adds will be written to a script for a domain controller.' }) $result = [ordered]@{ Status = $null; TenantId = $tenantId; TaskName = $taskName; RunAs = $RunAs; Sides = @($Side) MaxAdds = $MaxAdds; FirstRun = $At.ToString('yyyy-MM-dd HH:mm'); EveryHours = $EveryHours Command = $jobCommand; Problems = @($problems); Notes = $notes } if ($problems.Count) { $result.Status = 'NotReady'; return [pscustomobject]$result } if (-not $Execute) { $result.Status = 'PlanOnly'; return [pscustomobject]$result } if (-not $PSCmdlet.ShouldProcess("tenant $tenantId", "Enable scheduled coverage (cap $MaxAdds adds) and register task '$taskName' as $RunAs every $EveryHours h")) { return } Register-NSPConditionalAccessScheduledTask -TaskName $taskName -Arguments $arguments -RunAs $RunAs -At $At -EveryHours $EveryHours -Confirm:$false $coverage = [ordered]@{ ExemptGroup = $settings.ExemptGroup; IncludeGuests = $settings.IncludeGuests; MaxAdds = $settings.MaxAdds; MaxAddsPercent = $settings.MaxAddsPercent Scheduled = [ordered]@{ Enabled = $true; Sides = @($Side); MaxAdds = $MaxAdds; TaskName = $taskName; InstalledAtUtc = (Get-Date).ToUniversalTime().ToString('o') } } $content | Add-Member -NotePropertyName Coverage -NotePropertyValue ([pscustomobject]$coverage) -Force Save-NSPConditionalAccessJson -InputObject $content -Path $profilePath $result.Status = 'Installed' [pscustomobject]$result } |