Private/Get-NSPConditionalAccessOnPremGroupScript.ps1
|
function Get-NSPConditionalAccessOnPremGroupScript { <# .SYNOPSIS The text of the script that creates a Conditional Access group in Active Directory, for the operator to run on-premises. .DESCRIPTION The script uses the public NSP modules, installed from the PowerShell Gallery when missing: - NSP.Bootstrap: Invoke-NSPElevated (right-click > Run with PowerShell relaunches it elevated; NSP_NO_ELEVATE=1 keeps it in the session) and Install-NSPModule. - NSP.ActiveDirectory: Select-NSPADOrganizationalUnit (browse to the OU when -OrganizationalUnit is not given) and New-NSPADGroup (idempotent, by sAMAccountName). - NSP.Toolkit: its dry-run switch, turned off only for a real run; -WhatIf creates nothing. Only loading NSP.Bootstrap itself is inline. Without Gallery access the script stops and says what to install. It offers a delta sync on the Entra Connect server, and waits for Enter before a window it opened closes. #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory)][string]$Name, [string]$Purpose = 'Conditional Access group managed by NSP.M365.ConditionalAccess.' ) $template = @' <# Generated by NSP.M365.ConditionalAccess on __DATE__. Creates the Active Directory group '__NAME__' (global security group) for Conditional Access. Right-click > Run with PowerShell, or run it from PowerShell. It asks to elevate when needed, and lets you browse to the OU unless -OrganizationalUnit is given. -WhatIf shows what it would do. It uses NSP.Bootstrap and NSP.ActiveDirectory, installing them from the PowerShell Gallery for the current user when missing. Afterwards the group syncs to Entra ID; then, in the dashboard, update the policies that use it (Travel > Update travel policies, or plan again). #> [CmdletBinding(SupportsShouldProcess)] param( # Distinguished name of the OU, for example OU=Groups,DC=contoso,DC=local. [string]$OrganizationalUnit ) $groupName = '__NAME__' $description = '__PURPOSE__' # Wait before a window this script opened closes (Run with PowerShell, or the elevated relaunch). $ownWindow = @([Environment]::GetCommandLineArgs() | Where-Object { $_ -match '^-(File|Command|EncodedCommand)$' }).Count -gt 0 function Wait-Close { if ($ownWindow) { Read-Host 'Press Enter to close' | Out-Null } } function Stop-WithError { param([string]$Message) Write-Host "Failed: $Message" -ForegroundColor Red Wait-Close exit 1 } # NSP.Bootstrap brings elevation and module installs; it is the one module loaded by hand. try { if (-not (Get-Module NSP.Bootstrap | Where-Object { $_.Version -ge [version]'0.1.4' })) { if (-not (Get-Module -ListAvailable NSP.Bootstrap | Where-Object { $_.Version -ge [version]'0.1.4' })) { Write-Host 'Installing NSP.Bootstrap from the PowerShell Gallery (current user)...' [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 if (-not (Get-PackageProvider -ListAvailable -Name NuGet -ErrorAction SilentlyContinue)) { Install-PackageProvider -Name NuGet -MinimumVersion '2.8.5.201' -Scope CurrentUser -Force | Out-Null } Install-Module -Name NSP.Bootstrap -MinimumVersion '0.1.4' -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop } Import-Module NSP.Bootstrap -MinimumVersion '0.1.4' -ErrorAction Stop } } catch { Stop-WithError "NSP.Bootstrap could not be loaded: $($_.Exception.Message). Without PowerShell Gallery access, install NSP.Bootstrap and NSP.ActiveDirectory on this server another way, then run this again." } $relaunch = @() if ($OrganizationalUnit) { $relaunch += @('-OrganizationalUnit', $OrganizationalUnit) } if ($WhatIfPreference) { $relaunch += '-WhatIf' } try { if (Invoke-NSPElevated -ScriptPath $PSCommandPath -ArgumentList $relaunch) { return } } catch { Stop-WithError "Not elevated, and elevation was refused or failed: $($_.Exception.Message)" } try { foreach ($module in @(@('NSP.Toolkit', '0.1.1'), @('NSP.ActiveDirectory', '0.1.4'))) { if (-not (Get-Module $module[0] | Where-Object { $_.Version -ge [version]$module[1] })) { Install-NSPModule -Name $module[0] -MinimumVersion $module[1] Import-Module $module[0] -MinimumVersion $module[1] -Global -ErrorAction Stop } } if (-not (Get-Command Get-ADGroup -ErrorAction SilentlyContinue)) { Import-Module ActiveDirectory -ErrorAction Stop } $sam = $groupName -replace '[^A-Za-z0-9_\-]', '' $existing = Get-ADGroup -LDAPFilter "(sAMAccountName=$sam)" -ErrorAction SilentlyContinue if ($existing) { Write-Host "The group '$groupName' already exists: $($existing.DistinguishedName). Nothing to do." -ForegroundColor Yellow } else { $chosen = -not $OrganizationalUnit if ($chosen) { Write-Host "Where should the group '$groupName' go? Browse to its OU." -ForegroundColor Cyan $OrganizationalUnit = Select-NSPADOrganizationalUnit if (-not $OrganizationalUnit) { Write-Host 'Cancelled. Nothing was created.' -ForegroundColor Yellow; Wait-Close; return } } else { Get-ADOrganizationalUnit -Identity $OrganizationalUnit -ErrorAction Stop | Out-Null } if ($chosen -and -not $WhatIfPreference) { $confirm = Read-Host "Create '$groupName' in $OrganizationalUnit? [Y/n]" if ($confirm -and $confirm -notmatch '^[Yy]') { Write-Host 'Cancelled. Nothing was created.' -ForegroundColor Yellow; Wait-Close; return } } Set-NSPDryRun -Enabled ([bool]$WhatIfPreference) $dn = New-NSPADGroup -Name $groupName -Path $OrganizationalUnit -GroupScope Global -GroupCategory Security -Description $description -WhatIf:$WhatIfPreference if (-not $WhatIfPreference) { Write-Host "Created: $dn" -ForegroundColor Green } } if (-not $WhatIfPreference -and (Get-Command Start-ADSyncSyncCycle -ErrorAction SilentlyContinue)) { $sync = Read-Host 'Entra Connect is on this server. Run a delta sync now? [Y/n]' if (-not $sync -or $sync -match '^[Yy]') { Start-ADSyncSyncCycle -PolicyType Delta | Out-Null; Write-Host 'Delta sync started.' -ForegroundColor Green } } else { Write-Host 'Next: run a delta sync on the Entra Connect server (Start-ADSyncSyncCycle -PolicyType Delta), or wait for the next one.' } Write-Host 'Then, in the dashboard, update the policies that use the group (Travel > Update travel policies, or plan again).' } catch { Stop-WithError $_.Exception.Message } Wait-Close '@ $template.Replace('__DATE__', (Get-Date).ToString('yyyy-MM-dd')). Replace('__NAME__', ($Name -replace "'", "''")). Replace('__PURPOSE__', ($Purpose -replace "'", "''")) } |