Skip to content

PowerShell Setup: Control 1.22 — Information Barriers for AI Agents

Read the FSI PowerShell baseline first

Before running any command in this playbook, read the PowerShell Authoring Baseline for FSI Implementations. It is the canonical source for module version pinning, sovereign-cloud (GCC / GCC High / DoD) endpoints, mutation safety (-WhatIf / SupportsShouldProcess), Dataverse compatibility, and SHA-256 evidence emission. Snippets below show abbreviated patterns; the baseline is authoritative.

Last Updated: April 2026 Modules Required: ExchangeOnlineManagement (Compliance PowerShell — Connect-IPPSSession), Microsoft.Graph (for HR-of-record attribute reads and segment-coverage reporting), optional PnP.PowerShell v2+ (for SharePoint site-segment validation)


Prerequisites

# Pin module versions in regulated tenants. Substitute CAB-approved versions.
Install-Module -Name ExchangeOnlineManagement -RequiredVersion '<approved-version>' `
    -Repository PSGallery -Scope CurrentUser -AllowClobber -AcceptLicense

Install-Module -Name Microsoft.Graph -RequiredVersion '<approved-version>' `
    -Repository PSGallery -Scope CurrentUser -AllowClobber -AcceptLicense

# Optional, for SharePoint site-segment validation:
Install-Module -Name PnP.PowerShell -RequiredVersion '<approved-version>' `
    -Repository PSGallery -Scope CurrentUser -AllowClobber -AcceptLicense

Compliance PowerShell (Connect-IPPSSession) is Windows PowerShell 5.1 Desktop edition for some IB cmdlets and PowerShell 7+ for others depending on the ExchangeOnlineManagement build. Verify the cmdlet's edition requirement before automating.

All snippets assume an interactive admin session. For unattended automation, use a certificate-based service principal with the Compliance Administrator role or the Information Barriers Manager role assignment.


Script 1 — Pre-Change Baseline Capture

Always capture state before modifying IB. This output is the rollback reference and the evidence baseline for change control (FINRA 3110, SOX 404).

<#
.SYNOPSIS
    Captures Information Barriers baseline state for Control 1.22 evidence.

.DESCRIPTION
    Records IB mode, segments, policies, and last application status.
    Writes timestamped CSV/JSON outputs and emits SHA-256 hashes for
    chain-of-custody (SEC 17a-4 / FINRA 4511 alignment).

.EXAMPLE
    .\Get-IBBaseline.ps1 -OutputPath '.\evidence\1.22\baseline'
#>
[CmdletBinding()]
param(
    [Parameter(Mandatory)][string]$OutputPath
)

$ErrorActionPreference = 'Stop'
if (-not (Test-Path $OutputPath)) { New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null }
$timestamp = Get-Date -Format 'yyyyMMdd-HHmmss'

Connect-IPPSSession -ShowBanner:$false

$mode = Get-InformationBarrierMode
$segments = Get-OrganizationSegment
$policies = Get-InformationBarrierPolicy
$status = Get-InformationBarrierPoliciesApplicationStatus

$mode    | ConvertTo-Json -Depth 5 | Out-File "$OutputPath\IB-Mode-$timestamp.json"   -Encoding utf8
$segments| Select-Object Name, UserGroupFilter, Type, ExoSegmentId, CreatedBy, WhenCreated, WhenChanged |
    Export-Csv "$OutputPath\IB-Segments-$timestamp.csv" -NoTypeInformation -Encoding utf8
$policies| Select-Object Name, AssignedSegment, SegmentsAllowed, SegmentsBlocked, State, ModerationAllowed, WhenChanged |
    Export-Csv "$OutputPath\IB-Policies-$timestamp.csv" -NoTypeInformation -Encoding utf8
$status  | ConvertTo-Json -Depth 5 | Out-File "$OutputPath\IB-AppStatus-$timestamp.json" -Encoding utf8

# SHA-256 chain-of-custody hashes
Get-ChildItem $OutputPath -Filter "*$timestamp*" | ForEach-Object {
    $hash = Get-FileHash -Path $_.FullName -Algorithm SHA256
    [PSCustomObject]@{ File = $_.Name; SHA256 = $hash.Hash; CapturedAtUtc = (Get-Date).ToUniversalTime() }
} | Export-Csv "$OutputPath\Evidence-Manifest-$timestamp.csv" -NoTypeInformation -Encoding utf8

Disconnect-ExchangeOnline -Confirm:$false
Write-Host "Baseline captured to $OutputPath" -ForegroundColor Green

Script 2 — Create Organization Segments

<#
.SYNOPSIS
    Creates IB organization segments for FSI ethical walls.

.PARAMETER WhatIf
    Standard ShouldProcess support — preview without mutation.

.EXAMPLE
    .\New-IBSegments.ps1 -WhatIf
    .\New-IBSegments.ps1
#>
[CmdletBinding(SupportsShouldProcess)]
param()

$ErrorActionPreference = 'Stop'
Connect-IPPSSession -ShowBanner:$false

$segments = @(
    @{ Name = 'IB-Research';            Filter = "Department -eq 'Equity Research'" }
    @{ Name = 'IB-Trading';             Filter = "Department -eq 'Sales and Trading'" }
    @{ Name = 'IB-IB-Public';           Filter = "Department -eq 'Investment Banking' -and CompanyName -eq 'Capital Markets'" }
    @{ Name = 'IB-IB-Private';          Filter = "Department -eq 'Investment Banking' -and CompanyName -eq 'M&A'" }
    @{ Name = 'IB-Muni-Underwriting';   Filter = "Department -eq 'Municipal Underwriting'" }
    @{ Name = 'IB-Muni-Advisory';       Filter = "Department -eq 'Municipal Advisory'" }
    @{ Name = 'IB-Compliance';          Filter = "Department -eq 'Compliance'" }
)

foreach ($s in $segments) {
    $existing = Get-OrganizationSegment -Identity $s.Name -ErrorAction SilentlyContinue
    if ($existing) {
        Write-Host "[EXISTS] $($s.Name)" -ForegroundColor Yellow
        continue
    }
    if ($PSCmdlet.ShouldProcess($s.Name, "New-OrganizationSegment")) {
        New-OrganizationSegment -Name $s.Name -UserGroupFilter $s.Filter | Out-Null
        Write-Host "[CREATED] $($s.Name)" -ForegroundColor Green
    }
}

Disconnect-ExchangeOnline -Confirm:$false

Script 3 — Create IB Policies (Multi-Segment Allow-List Pattern)

Multi-Segment mode policies are allow-lists. The -SegmentsAllowed array names every segment the assigned segment may communicate with. Misconfiguration here is the most common cause of unintended barrier failures.

<#
.SYNOPSIS
    Creates IB policies in MultiSegment mode for FSI ethical walls.

.NOTES
    Verifies Get-InformationBarrierMode returns 'MultiSegment' before creating
    allow-list policies. Falls back with an error in other modes.
#>
[CmdletBinding(SupportsShouldProcess)]
param()

$ErrorActionPreference = 'Stop'
Connect-IPPSSession -ShowBanner:$false

$mode = (Get-InformationBarrierMode).Identity
if ($mode -ne 'MultiSegment') {
    throw "IB mode is '$mode'. This script targets MultiSegment. Migrate first or use the SingleSegment block-list variant."
}

$policies = @(
    @{ Name = 'Policy-IB-Research';          Assigned = 'IB-Research';          Allowed = @('IB-Research','IB-Compliance') }
    @{ Name = 'Policy-IB-Trading';           Assigned = 'IB-Trading';           Allowed = @('IB-Trading','IB-Compliance') }
    @{ Name = 'Policy-IB-IB-Public';         Assigned = 'IB-IB-Public';         Allowed = @('IB-IB-Public','IB-Compliance') }
    @{ Name = 'Policy-IB-IB-Private';        Assigned = 'IB-IB-Private';        Allowed = @('IB-IB-Private','IB-Compliance') }
    @{ Name = 'Policy-IB-Muni-Underwriting'; Assigned = 'IB-Muni-Underwriting'; Allowed = @('IB-Muni-Underwriting','IB-Compliance') }
    @{ Name = 'Policy-IB-Muni-Advisory';     Assigned = 'IB-Muni-Advisory';     Allowed = @('IB-Muni-Advisory','IB-Compliance') }
    @{ Name = 'Policy-IB-Compliance';        Assigned = 'IB-Compliance';        Allowed = @('IB-Research','IB-Trading','IB-IB-Public','IB-IB-Private','IB-Muni-Underwriting','IB-Muni-Advisory','IB-Compliance') }
)

foreach ($p in $policies) {
    $existing = Get-InformationBarrierPolicy -Identity $p.Name -ErrorAction SilentlyContinue
    if ($existing) {
        Write-Host "[EXISTS] $($p.Name) — review before re-creating" -ForegroundColor Yellow
        continue
    }
    if ($PSCmdlet.ShouldProcess($p.Name, "New-InformationBarrierPolicy (MultiSegment allow-list)")) {
        New-InformationBarrierPolicy -Name $p.Name `
            -AssignedSegment $p.Assigned `
            -SegmentsAllowed $p.Allowed `
            -State Active | Out-Null
        Write-Host "[CREATED] $($p.Name)" -ForegroundColor Green
    }
}

Write-Host "`nReview policies before applying." -ForegroundColor Cyan
Get-InformationBarrierPolicy | Select-Object Name, AssignedSegment, SegmentsAllowed, State | Format-Table -AutoSize

Disconnect-ExchangeOnline -Confirm:$false

Script 4 — Apply IB Policies and Track Status

<#
.SYNOPSIS
    Applies IB policies and waits up to N hours for completion.

.PARAMETER MaxWaitHours
    Maximum hours to poll Get-InformationBarrierPoliciesApplicationStatus.
    Default 6; production tenants frequently take 24–72 hours.

.EXAMPLE
    .\Start-IBApplication.ps1 -MaxWaitHours 24
#>
[CmdletBinding(SupportsShouldProcess)]
param(
    [int]$MaxWaitHours = 6,
    [int]$PollSeconds = 300
)

$ErrorActionPreference = 'Stop'
Connect-IPPSSession -ShowBanner:$false

if ($PSCmdlet.ShouldProcess('Tenant', 'Start-InformationBarrierPoliciesApplication')) {
    Start-InformationBarrierPoliciesApplication
    Write-Host "Application started at $(Get-Date -Format 'u')" -ForegroundColor Cyan
}

$deadline = (Get-Date).AddHours($MaxWaitHours)
do {
    Start-Sleep -Seconds $PollSeconds
    $status = Get-InformationBarrierPoliciesApplicationStatus | Sort-Object StartTime -Descending | Select-Object -First 1
    Write-Host "[$(Get-Date -Format 'u')] Status: $($status.Status)  Identity: $($status.Identity)" -ForegroundColor DarkCyan
} while ($status.Status -in @('NotStarted','InProgress') -and (Get-Date) -lt $deadline)

if ($status.Status -ne 'Completed') {
    Write-Warning "Did not reach Completed within $MaxWaitHours hours. Current status: $($status.Status)"
    exit 2
}

Write-Host "[PASS] Application Completed at $($status.EndTime)" -ForegroundColor Green
Disconnect-ExchangeOnline -Confirm:$false

Script 5 — Segment Coverage Report (Identify Segment-less Users)

Users without a segment bypass IB enforcement and are the highest-impact residual risk. This report identifies them so they can be remediated before policy application.

<#
.SYNOPSIS
    Identifies users not covered by any IB segment.

.DESCRIPTION
    Cross-references all enabled member users in Entra ID against current
    IB segment filters and emits a CSV of segment-less users for remediation.

.EXAMPLE
    .\Export-IBSegmentCoverage.ps1 -OutputPath '.\evidence\1.22\coverage'
#>
[CmdletBinding()]
param(
    [Parameter(Mandatory)][string]$OutputPath
)

$ErrorActionPreference = 'Stop'
if (-not (Test-Path $OutputPath)) { New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null }
$timestamp = Get-Date -Format 'yyyyMMdd-HHmmss'

Connect-MgGraph -Scopes 'User.Read.All' -NoWelcome | Out-Null
$users = Get-MgUser -All -Filter "accountEnabled eq true and userType eq 'Member'" `
    -Property Id,UserPrincipalName,DisplayName,Department,CompanyName,JobTitle

# Heuristic: an HR-of-record-driven segmentation requires Department to be populated.
$uncovered = $users | Where-Object { [string]::IsNullOrWhiteSpace($_.Department) }

$uncovered | Select-Object UserPrincipalName, DisplayName, Department, CompanyName, JobTitle |
    Export-Csv "$OutputPath\IB-UncoveredUsers-$timestamp.csv" -NoTypeInformation -Encoding utf8

$summary = [PSCustomObject]@{
    TotalEnabledMembers = $users.Count
    Uncovered           = $uncovered.Count
    CoveragePercent     = if ($users.Count -gt 0) { [math]::Round((($users.Count - $uncovered.Count) / $users.Count) * 100, 2) } else { 0 }
    GeneratedAtUtc      = (Get-Date).ToUniversalTime()
}
$summary | ConvertTo-Json | Out-File "$OutputPath\IB-CoverageSummary-$timestamp.json" -Encoding utf8

Write-Host "Coverage: $($summary.CoveragePercent)% — $($summary.Uncovered) uncovered users" -ForegroundColor Cyan
Disconnect-MgGraph | Out-Null

Script 6 — Validate Configuration End-to-End

<#
.SYNOPSIS
    Validates Control 1.22 configuration is in the intended state.

.DESCRIPTION
    Returns 0 on PASS; non-zero on FAIL with reason logged.
    Designed for unattended scheduled runs feeding compliance dashboards.
#>
[CmdletBinding()]
param(
    [string]$ExpectedMode = 'MultiSegment',
    [string[]]$RequiredSegments = @('IB-Research','IB-Trading','IB-IB-Public','IB-IB-Private','IB-Muni-Underwriting','IB-Muni-Advisory','IB-Compliance'),
    [decimal]$MinimumCoveragePercent = 99.5
)

$ErrorActionPreference = 'Stop'
$failures = @()

Connect-IPPSSession -ShowBanner:$false

$mode = (Get-InformationBarrierMode).Identity
if ($mode -ne $ExpectedMode) { $failures += "IB mode is '$mode'; expected '$ExpectedMode'." }

$segments = (Get-OrganizationSegment).Name
$missing = $RequiredSegments | Where-Object { $_ -notin $segments }
if ($missing) { $failures += "Missing segments: $($missing -join ', ')." }

$active = Get-InformationBarrierPolicy | Where-Object State -eq 'Active'
if ($active.Count -lt $RequiredSegments.Count) {
    $failures += "Active policy count ($($active.Count)) less than required segment count ($($RequiredSegments.Count))."
}

$last = Get-InformationBarrierPoliciesApplicationStatus | Sort-Object StartTime -Descending | Select-Object -First 1
if ($last.Status -ne 'Completed') { $failures += "Last application status: $($last.Status)." }

Disconnect-ExchangeOnline -Confirm:$false

if ($failures) {
    $failures | ForEach-Object { Write-Host "[FAIL] $_" -ForegroundColor Red }
    exit 1
}
Write-Host "[PASS] Control 1.22 validation succeeded" -ForegroundColor Green
exit 0

Sovereign-Cloud Connection Notes

For GCC High and DoD tenants, Connect-IPPSSession requires explicit endpoints:

# GCC High
Connect-IPPSSession `
    -ConnectionUri 'https://ps.compliance.protection.office365.us/powershell-liveid/' `
    -AzureADAuthorizationEndpointUri 'https://login.microsoftonline.us/common'

# DoD
Connect-IPPSSession `
    -ConnectionUri 'https://l5.ps.compliance.protection.office365.us/powershell-liveid/' `
    -AzureADAuthorizationEndpointUri 'https://login.microsoftonline.us/common'

Refer to the PowerShell Authoring Baseline §6 for current sovereign-cloud endpoint tables.


Rollback

If a misapplied policy must be rolled back:

  1. Set-InformationBarrierPolicy -Identity <name> -State Inactive to deactivate without deletion (preserves audit history).
  2. Start-InformationBarrierPoliciesApplication to push the deactivation.
  3. Confirm Get-InformationBarrierPoliciesApplicationStatus reaches Completed.
  4. Only delete a policy after Compliance sign-off; deletion is permanent.

Back to Control 1.22 | Portal Walkthrough | Verification Testing | Troubleshooting