Skip to content

PowerShell Setup: Control 2.22 - Inactivity Timeout Enforcement

Parent Control: 2.22 - Inactivity Timeout Enforcement

Last Updated: February 2026 Module Requirements: Az.Accounts, PowerShell 7.0+ Estimated Time: 30 minutes for setup; 5 minutes per remediation run

Prerequisites

  • PowerShell 7.x or later installed
  • Az.Accounts module installed (Install-Module Az.Accounts)
  • Authenticated session via Connect-AzAccount (service principal or interactive)
  • Service principal with Power Platform Admin role or delegated environment admin
  • App registration with https://api.bap.microsoft.com/.default scope granted (for service principal auth)
  • Environment names (EnvironmentName, not display name) identified for remediation

Script: Set-InactivityTimeout.ps1

Script Location

The script is located at scripts/governance/Set-InactivityTimeout.ps1 in the FSI-AgentGov repository. Navigate to the scripts/governance/ directory or adjust paths in the commands below accordingly.

Synopsis

Configures the inactivity timeout duration for a Power Platform environment via the BAP Admin API privacy settings endpoint. Uses a GET-PATCH-GET pattern to read current state, apply changes, and verify. Supports -WhatIf for preview mode, optional Dataverse audit record writing, and evidence packaging with SHA-256 integrity hashing.

Parameters

Parameter Type Required Default Description
-EnvironmentName String Yes Power Platform Environment Name (canonical GUID, not display name)
-TimeoutDuration Int32 No 120 Inactivity timeout duration in minutes (valid range: 5-120)
-WarningDuration Int32 No 5 Warning notification duration in minutes before timeout (valid range: 1-30; must be less than -TimeoutDuration)
-DataverseUrl String No Dataverse environment URL for writing remediation audit records (e.g., https://org12345.crm.dynamics.com/)
-OutputFormat String No Object Output format: Table, JSON, or Object
-OutputPath String No File path to export JSON results
-IncludeEvidence Switch No Computes SHA-256 integrity hash over results for evidence packaging
-WhatIf Switch No Preview mode — displays current vs. target configuration without making changes
-Verbose Switch No Detailed output including API request/response details

Authentication

The script requires an authenticated Azure session via Connect-AzAccount before execution. It obtains BAP API tokens automatically using Get-AzAccessToken. No -ClientId, -TenantId, or -ClientSecret parameters are needed — authentication is handled by the pre-existing session.

Example Commands

Preview changes (WhatIf mode)

# Authenticate first (one-time per session)
Connect-AzAccount -ServicePrincipal `
    -ApplicationId $clientId `
    -CertificateThumbprint $thumbprint `
    -TenantId $tenantId

# Preview what would change
.\Set-InactivityTimeout.ps1 `
    -EnvironmentName "d1234567-abcd-ef01-2345-6789abcdef01" `
    -TimeoutDuration 60 `
    -WhatIf

Expected output:

What if: Performing the operation "Set inactivity timeout to 60 min with 5 min warning" on target "d1234567-abcd-ef01-2345-6789abcdef01".

Apply remediation to a single environment (Zone 3)

.\Set-InactivityTimeout.ps1 `
    -EnvironmentName "d1234567-abcd-ef01-2345-6789abcdef01" `
    -TimeoutDuration 60 `
    -WarningDuration 10 `
    -Verbose

Apply remediation with Dataverse audit record

.\Set-InactivityTimeout.ps1 `
    -EnvironmentName "d1234567-abcd-ef01-2345-6789abcdef01" `
    -TimeoutDuration 60 `
    -DataverseUrl "https://org12345.crm.dynamics.com/" `
    -IncludeEvidence `
    -OutputFormat JSON `
    -OutputPath .\evidence\timeout-remediation.json

Bulk remediation from CSV

# CSV file must include a header row.
# Required columns: EnvironmentName, TimeoutDuration
# Optional column: WarningDuration (defaults to 5 if omitted)
#
# Example CSV (non-compliant-environments.csv):
# EnvironmentName,TimeoutDuration,WarningDuration
# d1234567-abcd-ef01-2345-6789abcdef01,60,10
# e2345678-bcde-f012-3456-789abcdef012,120,15

Import-Csv ".\non-compliant-environments.csv" | ForEach-Object {
    $params = @{
        EnvironmentName = $_.EnvironmentName
        TimeoutDuration = [int]$_.TimeoutDuration
    }
    if ($_.WarningDuration) {
        $params['WarningDuration'] = [int]$_.WarningDuration
    }
    .\Set-InactivityTimeout.ps1 @params
}

Bulk remediation with WhatIf preview

Import-Csv ".\non-compliant-environments.csv" | ForEach-Object {
    $params = @{
        EnvironmentName = $_.EnvironmentName
        TimeoutDuration = [int]$_.TimeoutDuration
        WhatIf          = $true
    }
    if ($_.WarningDuration) {
        $params['WarningDuration'] = [int]$_.WarningDuration
    }
    .\Set-InactivityTimeout.ps1 @params
}

API Reference

GET — Retrieve Current Privacy Settings

GET https://api.bap.microsoft.com/providers/Microsoft.BusinessAppPlatform/scopes/admin/environments/{EnvironmentName}/settings/privacy?api-version=2021-04-01

Authorization: Bearer {access_token}

Response (example):

{
  "properties": {
    "InactivityTimeoutEnabled": true,
    "InactivityTimeoutInMinutes": 120,
    "InactivityWarningInMinutes": 10
  }
}

Session Expiration via BAP Admin API

The BAP Admin API privacy settings endpoint also returns session expiration properties alongside inactivity timeout settings. When querying an environment's privacy settings using the GET endpoint above, the response may include session expiration fields. Organizations can use this endpoint to programmatically verify both inactivity timeout and session expiration configuration in a single API call. Refer to the existing patterns in the GET and PATCH examples above for authentication and request format.

PATCH — Update Privacy Settings

PATCH https://api.bap.microsoft.com/providers/Microsoft.BusinessAppPlatform/scopes/admin/environments/{EnvironmentName}/settings/privacy?api-version=2021-04-01

Authorization: Bearer {access_token}
Content-Type: application/json

{
  "properties": {
    "InactivityTimeoutEnabled": true,
    "InactivityTimeoutInMinutes": 60,
    "InactivityWarningInMinutes": 5
  }
}

Authentication Setup

Service Principal Configuration

  1. Register an application in Microsoft Entra ID
  2. Grant the application the Power Platform Admin role, or assign Environment Admin per environment
  3. Add the API permission scope: https://api.bap.microsoft.com/.default
  4. Grant admin consent for the API permission
  5. Create a client secret or upload a certificate for authentication

Authenticating Before Running the Script

The script uses Get-AzAccessToken internally to obtain BAP API tokens. You must authenticate via Connect-AzAccount before running the script:

# Option 1: Service principal with certificate (recommended for automation)
Connect-AzAccount -ServicePrincipal `
    -ApplicationId $clientId `
    -CertificateThumbprint $thumbprint `
    -TenantId $tenantId

# Option 2: Service principal with client secret
$secureSecret = ConvertTo-SecureString $clientSecret -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($clientId, $secureSecret)
Connect-AzAccount -ServicePrincipal -Credential $credential -TenantId $tenantId

# Option 3: Interactive (for manual remediation)
Connect-AzAccount -TenantId $tenantId

Token Lifecycle

The script obtains tokens automatically via Get-AzAccessToken. If your session expires during a long bulk run, re-run Connect-AzAccount and retry.

Obtaining a Token for Diagnostic Commands

For standalone API calls outside the script (e.g., troubleshooting diagnostics):

$tokenResult = Get-AzAccessToken -ResourceUrl "https://api.bap.microsoft.com"
$token = if ($tokenResult.Token -is [securestring]) { $tokenResult.Token | ConvertFrom-SecureString -AsPlainText } else { $tokenResult.Token }

Error Handling

Error Cause Resolution
401 Unauthorized Token expired or invalid scope Re-authenticate; verify https://api.bap.microsoft.com/.default scope
403 Forbidden Insufficient permissions Verify service principal has Power Platform Admin or Environment Admin role
404 Not Found Invalid EnvironmentName Use the canonical EnvironmentName (GUID format), not the display name
429 Too Many Requests API rate limiting Implement retry with exponential backoff; reduce concurrency for bulk runs
Other HTTP errors Various API issues Check the error message for details; verify API endpoint availability and request format

Evidence Hash Verification

When -IncludeEvidence is used, the script computes a SHA-256 integrity hash over the result object. The hash is computed on compressed JSON (no whitespace). To verify a previously exported evidence file:

# 1. Read the exported evidence JSON
$evidence = Get-Content .\evidence\timeout-remediation.json -Raw | ConvertFrom-Json

# 2. Capture the recorded hash, then null it out for recomputation
$recordedHash = $evidence.Metadata.IntegrityHash
$evidence.Metadata.IntegrityHash = $null

# 3. Recompute the hash using compressed JSON (must match original format)
$json = $evidence | ConvertTo-Json -Depth 10 -Compress
$hashBytes = [System.Security.Cryptography.SHA256]::Create().ComputeHash(
    [System.Text.Encoding]::UTF8.GetBytes($json)
)
$computedHash = [BitConverter]::ToString($hashBytes) -replace '-'

# 4. Compare
if ($computedHash -eq $recordedHash) {
    Write-Host "Evidence integrity verified" -ForegroundColor Green
} else {
    Write-Warning "Hash mismatch — evidence may have been modified"
}

Compression Required

The hash is computed on compressed JSON (ConvertTo-Json -Compress). Using pretty-printed JSON for verification will produce a different hash. If hash verification fails on an unmodified file, ensure you are using the same PowerShell version that generated the evidence — JSON property ordering may vary between PowerShell versions.


Next Steps


Updated: February 2026 | Version: v1.3