Public/Add-NSPConditionalAccessPolicyTarget.ps1
|
function Add-NSPConditionalAccessPolicyTarget { <# .SYNOPSIS Adds a user or group to one policy's includes or excludes, or a service account to every policy's exclusions, in the tenant profile. .DESCRIPTION Offline; changes only the tenant profile. The next deployment plan updates the policy, and the update is reviewed, approved, and undoable like any other. -Field says what the addition does: - ExcludeUsers / ExcludeGroups: the policy leaves them out. From a block policy, that lets them through; from an MFA policy, they no longer need MFA from it. - IncludeUsers / IncludeGroups: the policy also applies to them, which restricts them. Refused for a policy that already includes every user. Additions sit on top of the definition's own targets, which are not changed here (breakglass, the group slots). They stay through a profile rebuild. -ServiceAccount adds an account to the profile's service accounts instead: every policy that excludes {ServiceAccounts} (most of them) leaves it out. A breakglass account is refused; it is already excluded from every policy. .EXAMPLE Add-NSPConditionalAccessPolicyTarget -TenantProfile $profilePath -PolicyId 'NSP-BASE-GEO-01' -Field ExcludeUsers -Id $user.id -Label $user.userPrincipalName .EXAMPLE Add-NSPConditionalAccessPolicyTarget -TenantProfile $profilePath -ServiceAccount -Id $user.id -Label 'scanner@contoso.example' #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Policy')] param( [Parameter(Mandatory)][string]$TenantProfile, [Parameter(Mandatory, ParameterSetName = 'Policy')][string]$PolicyId, [Parameter(Mandatory, ParameterSetName = 'Policy')][ValidateSet('IncludeUsers', 'IncludeGroups', 'ExcludeUsers', 'ExcludeGroups')][string]$Field, [Parameter(Mandatory, ParameterSetName = 'ServiceAccount')][switch]$ServiceAccount, [Parameter(Mandatory)][ValidatePattern('^[0-9a-fA-F-]{36}$')][string]$Id, [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Label ) $content = Get-Content -LiteralPath $TenantProfile -Raw | ConvertFrom-Json $Id = $Id.ToLowerInvariant() $breakGlass = @(Get-NSPConditionalAccessMapValue $content.BreakGlass 'Ids' | ForEach-Object { ([string]$_).ToLowerInvariant() }) if ($Id -in $breakGlass) { throw "$Label is a breakglass account; every policy already excludes it." } if ($ServiceAccount) { if (-not $content.ServiceAccounts) { $content | Add-Member -NotePropertyName ServiceAccounts -NotePropertyValue ([pscustomobject]@{ Ids = @(); Labels = @() }) -Force } $ids = @($content.ServiceAccounts.Ids | Where-Object { $_ }) if ($Id -in @($ids | ForEach-Object { ([string]$_).ToLowerInvariant() })) { throw "$Label is already a service account." } $content.ServiceAccounts.Ids = @($ids) + $Id $content.ServiceAccounts.Labels = @(@($content.ServiceAccounts.Labels | Where-Object { $_ }) + $Label) $what = "Add service account $Label" } else { $definitions = @(Get-NSPConditionalAccessDefinition) + @(Get-NSPConditionalAccessTravelDefinition -TenantProfile $content) $definition = @($definitions | Where-Object { $_['Id'] -eq $PolicyId })[0] if (-not $definition) { throw "No policy definition $PolicyId. Known: $(@($definitions | ForEach-Object { $_['Id'] }) -join ', ')." } if ($Field -like 'Include*' -and @(Get-NSPConditionalAccessMapValue $definition['Users'] 'IncludeUsers') -contains 'All') { throw "$PolicyId already applies to every user, so no one can be added to it. Leave people out with ExcludeUsers or ExcludeGroups instead." } if (-not $content.PolicyTargets) { $content | Add-Member -NotePropertyName PolicyTargets -NotePropertyValue ([pscustomobject]@{}) -Force } if (-not $content.PolicyTargets.PSObject.Properties[$PolicyId]) { $content.PolicyTargets | Add-Member -NotePropertyName $PolicyId -NotePropertyValue ([pscustomobject]@{}) } $targets = $content.PolicyTargets.$PolicyId foreach ($other in 'IncludeUsers', 'IncludeGroups', 'ExcludeUsers', 'ExcludeGroups') { if (@($targets.$other | Where-Object { $_ -and ([string]$_.Id).ToLowerInvariant() -eq $Id }).Count) { throw "$Label is already added to $PolicyId as $other. Remove that first." } } $entry = [pscustomobject][ordered]@{ Id = $Id; Label = $Label; AddedAtUtc = (Get-Date).ToUniversalTime().ToString('o') } $targets | Add-Member -NotePropertyName $Field -NotePropertyValue @(@($targets.$Field | Where-Object { $_ }) + $entry) -Force $what = "Add $Label to $PolicyId $Field" } if ($PSCmdlet.ShouldProcess($TenantProfile, $what)) { Save-NSPConditionalAccessJson -InputObject $content -Path $TenantProfile } Get-NSPConditionalAccessPolicyTarget -TenantProfile $content | Where-Object { $_.Id -eq $Id -and ($ServiceAccount -or $_.PolicyId -eq $PolicyId) } } |