Automating Microsoft Teams Compliance Recording Policy Configuration with PowerShell

Automating Microsoft Teams Compliance Recording Policy Configuration with PowerShell

Microsoft Teams Compliance Recording is essential for organizations operating in regulated industries — such as finance, healthcare, and legal services — where capturing and retaining communications is mandatory.

While Teams provides the building blocks (policies, recording bots, and app instances), configuring them manually for every user or group quickly becomes unmanageable. The good news: with PowerShell, you can automate the entire process.

This guide explains how to write a PowerShell script that automates Teams compliance recording configuration — including looping through multiple users and groups for policy assignment and unassignment — using examples from a production-ready sample script.

Script Overview

The script performs these key steps:

  1. Reads configuration
  2. Checks prerequisites and installs required modules
  3. Connects securely to Microsoft Teams
  4. Creates or updates a bot application instance
  5. Creates or updates a compliance recording policy
  6. Loops through multiple users and groups to assign or remove the policy
  7. Logs all actions for auditing

Let’s walk through each section.

Reading Configuration

Define the configuration variables used by the script. You can also expose these values as input parameters so they can be passed in when the script is executed from the command line. Additionally, the lists of users and groups may be loaded from CSV files to make the script easier to maintain and automate at scale.

PowerShell
# Configuration variables used by the script
$BotUserPrincipalName = "bot@argusarchive.com"
$BotDisplayName = "Argus Archive Bot"
$BotApplicationId = "476dce8d-9229-6bc2-a2b5-bcac6c962e12"
$PolicyName = "Argus Compliance Policy"
$PolicyDescription = "Compliance recording policy for Argus Archive Bot"

$UserAssignments = @("john.doe@contoso.com", "jane.smith@contoso.com")
$GroupAssignments = @("sales@contoso.com", "trading@contoso.com")
$UserUnassignments = @()
$GroupUnassignments = @()

The following script shows how to read CSV files in PowerShell:

PowerShell
$UserAssignments = Import-Csv "users.csv" | ForEach-Object { $_.UserPrincipalName }

Docs:

Checking Prerequisites

Before you can manage Teams through PowerShell, ensure you have:

  • PowerShell 5.1 or later
  • Administrator privileges
  • NuGet package provider installed
  • MicrosoftTeams module installed

Use the following code to verify the prerequisites and install them if they are missing:

PowerShell
Write-Host "`nChecking prerequisites..." -ForegroundColor Cyan
try {
    # Check PowerShell version
    $psVersion = $PSVersionTable.PSVersion
    Write-Host "PowerShell Version: $psVersion"
    
    if ($psVersion.Major -lt 5) {
        throw "PowerShell 5.1 or later is required. Current version: $psVersion"
    }
 
    # Check if running as administrator
    $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
    $isAdmin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
    
    if (-not $isAdmin) {
        Write-Host "Warning: Not running as administrator. Some operations may fail." -ForegroundColor Yellow
    } else {
        Write-Host "Running with administrator privileges"
    }

    # Check if NuGet provider exists
    $nugetProvider = Get-PackageProvider -Name NuGet -ListAvailable -ErrorAction SilentlyContinue
    if (-not $nugetProvider -or $nugetProvider.Version -lt [Version]"2.8.5.201") {
        Write-Host "Installing NuGet package provider..."
        Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -ErrorAction Stop
        Write-Host "NuGet provider installed"
    }
    else {
        Write-Host "NuGet provider already installed (Version: $($nugetProvider.Version))"
    }
        
    # Check if MicrosoftTeams module exists
    $teamsModule = Get-Module -Name MicrosoftTeams -ListAvailable -ErrorAction SilentlyContinue
    if (-not $teamsModule) {
        Write-Host "Installing MicrosoftTeams module..."
        Install-Module MicrosoftTeams -Force -AllowClobber -ErrorAction Stop
        Write-Host "MicrosoftTeams module installed"
    }
    else {
        Write-Host "MicrosoftTeams module already installed (Version: $($teamsModule.Version))"
    }
}
catch {
    Write-Host "!! Prerequisite check/installation failed: $_" -ForegroundColor Red
    exit 1
}

Docs:

Connecting to Microsoft Teams

Connect to Teams with interactive login. The script below initiates an interactive login process where you need to provide your credentials to proceed.

PowerShell
Write-Host "`nConnecting to Microsoft Teams..." -ForegroundColor Cyan
try {
    Connect-MicrosoftTeams -ErrorAction Stop
    Write-Host "Connected to MicrosoftTeams successfully"
}
catch {
    Write-Host "!! Connection to Microsoft Teams failed: $_" -ForegroundColor Red
    exit 1
}

Docs: Connect-MicrosoftTeams

This cmdlet establishes an authenticated session, allowing the script to manage Teams configuration objects such as bots and policies.

If you are working on a fully automated solution, which does not require any user interaction, you can use the application based authentication with the Teams PowerShell module.

Managing the Compliance Bot Application Instance

Teams compliance recording requires an application instance of a bot that captures conversations.
The script checks if it exists, then creates it if missing:

PowerShell
Write-Host "`nManaging application instance..." -ForegroundColor Cyan
try {
    # Check if application instance already exists
    Write-Host "Checking for existing app instance: $BotUserPrincipalName"
    $existingApp = Get-CsOnlineApplicationInstance -Identity $BotUserPrincipalName -ErrorAction SilentlyContinue
    
    if ($existingApp) {
        $appObjectId = $existingApp.ObjectId
        Write-Host "Using existing app instance: $($existingApp.DisplayName) ($($existingApp.ObjectId))"
    }
    else {
        Write-Host "App instance not found. Creating new app instance..."
        $appInstance = New-CsOnlineApplicationInstance -UserPrincipalName $BotUserPrincipalName `
            -DisplayName $BotDisplayName `
            -ApplicationId $BotApplicationId `
            -ErrorAction Stop
        
        $appObjectId = $appInstance.ObjectId
        Write-Host "Created app instance: $($appInstance.DisplayName) (ObjectId: $appObjectId)"
    }
}
catch {
    Write-Host "!! Application instance management failed: $_" -ForegroundColor Red
    exit 1
}

Docs:

Creating or Updating the Compliance Policy

The compliance recording policy determines which users and groups are monitored and recorded by a specific application instance (bot). In the script, this step ensures the policy exists and that the application instance is correctly associated with it. However, this part is optional — if your application instance and compliance recording policy are already created and properly configured, you can skip it to keep the script focused solely on assigning or unassigning users and groups.

PowerShell
Write-Host "`nManaging compliance recording policy..." -ForegroundColor Cyan
try {
    # Check if policy exists
    Write-Host "Checking for existing compliance recording policy: $PolicyName"
    $existingPolicy = Get-CsTeamsComplianceRecordingPolicy -Identity "Tag:$PolicyName" -ErrorAction SilentlyContinue
        
    if ($existingPolicy) {
        Write-Host "Using existing compliance recording policy: $($existingPolicy.Identity)"
            
        # Check if our app is already registered with the policy
        $associatedApps = Get-CsTeamsComplianceRecordingApplication -Filter "Tag:$PolicyName*" -ErrorAction SilentlyContinue
        $appRegistered = $false
        if ($associatedApps) {
            Write-Host "Applications registered with this policy ($policyName):" -ForegroundColor Gray
            foreach ($app in $associatedApps) {
                Write-Host "   - Application ID: $($app.Id)" -ForegroundColor Gray
                if ($app.Id -eq $appObjectId) {
                    $appRegistered = $true
                    Write-Host "   -> Application is already registered with this policy" -ForegroundColor Green
                }
            }
        }
        else {
            Write-Host "No applications currently registered with this policy ($policyName)" -ForegroundColor Yellow
        }
            
        # If app not registered, update the policy
        if (-not $appRegistered) {
            Write-Host "Adding application to existing policy..."
            Set-CsTeamsComplianceRecordingPolicy -Identity "Tag:$PolicyName" `
                -RecordReroutedCalls $true `
                -ComplianceRecordingApplications @(
                New-CsTeamsComplianceRecordingApplication `
                    -Parent "Tag:$PolicyName" `
                    -Id $appObjectId
                -RequiredBeforeMeetingJoin $true
                -RequiredDuringMeeting $true
                -RequiredBeforeCallEstablishment $true
                -RequiredDuringCall $true
            ) -ErrorAction Stop
            Write-Host "Policy ($policyName) updated with new application"
        }
    }
    else {
        Write-Host "Policy not found. Creating new compliance recording policy: $PolicyName"
        New-CsTeamsComplianceRecordingPolicy -Enabled $true  -Description $PolicyDescription -Identity $PolicyName
        Set-CsTeamsComplianceRecordingPolicy -Identity "Tag:$PolicyName" `
            -RecordReroutedCalls $true `
            -ComplianceRecordingApplications @(
            New-CsTeamsComplianceRecordingApplication `
                -Parent "Tag:$PolicyName" `
                -Id $appObjectId
            -RequiredBeforeMeetingJoin $true
            -RequiredDuringMeeting $true
            -RequiredBeforeCallEstablishment $true
            -RequiredDuringCall $true
        ) -ErrorAction Stop
        Write-Host "Policy ($policyName) created and configured with application"
    }
}
catch {
    Write-Host "!! Policy management failed: $_" -ForegroundColor Red
    exit 1
}

Docs:

Assigning the Policy to Multiple Users and Groups

This is where automation shines — instead of assigning policies manually, you can loop through multiple users and groups defined in arrays or imported from CSV files.

Here’s how the script handles batch assignment:

PowerShell
Write-Host "`nAssigning compliance recording policy..." -ForegroundColor Cyan

# User assignments
if ($UserAssignments.Count -gt 0) {
    Write-Host "Processing $($UserAssignments.Count) user assignments..."
    foreach ($user in $UserAssignments) {
        try {
            Grant-CsTeamsComplianceRecordingPolicy -Identity $user -PolicyName $PolicyName -ErrorAction Stop 6>$null
            Write-Host "Successfully assigned policy to user: $user"
        }
        catch {
            Write-Host "!! Failed to assign policy to user: $user - Error: $($_.Exception.Message)" -ForegroundColor Red
        }
    }
}
       
# Group assignments
    if ($GroupAssignments.Count -gt 0) {
        Write-Host "Processing $($GroupAssignments.Count) group assignments..."
    foreach ($group in $GroupAssignments) {
        try {
            # Default to rank 1 if not specified elsewhere
            Grant-CsTeamsComplianceRecordingPolicy -Group $group -PolicyName $PolicyName -Rank 1 -ErrorAction Stop
            Write-Host "Successfully assigned policy to group: $group"
        }
        catch {
            Write-Host "!! Failed to assign policy to group: $user - Error: $($_.Exception.Message)" -ForegroundColor Red        
        }
    }
}

Docs:

This looping logic allows you to scale efficiently — for example, assigning policies to dozens of users or entire departments in a single run.

Unassigning Policies in Bulk

Similarly, you can unassign policies by looping through arrays of users or groups and setting the policy to $null:

PowerShell
Write-Host "`nUnassigning compliance recording policy..." -ForegroundColor Cyan

# User unassignments
if ($UserUnassignments.Count -gt 0) {
    Write-Host "`nProcessing $($UserUnassignments.Count) user unassignments..."
    foreach ($user in $UserUnassignments) {
        try {
            Grant-CsTeamsComplianceRecordingPolicy -Identity $user -PolicyName $null
            Write-Host "Successfully unassigned policies from user: $user" -ForegroundColor Green
        }
        catch {
            Write-Host "!! Failed to unassign policy from user: $user - Error: $($_.Exception.Message)" -ForegroundColor Red
        }
    }
}

# Group unassignments
if ($GroupUnassignments.Count -gt 0) {
    Write-Host "`nProcessing $($GroupUnassignments.Count) group unassignments..."
    foreach ($group in $GroupUnassignments) {
        try {
            Grant-CsTeamsComplianceRecordingPolicy -Group $group -PolicyName $null
            Write-Host "Successfully unassigned policies from group: $group" -ForegroundColor Green
        }
        catch {
            Write-Host "!! Failed to unassign policy from group: $group - Error: $($_.Exception.Message)" -ForegroundColor Red
        }
    }
}

This makes cleanup and access revocation just as automated as the initial setup.

Logging and Auditing

Every execution is logged using PowerShell transcripts for auditability. Put the following code snippet to the beginning of your script:

PowerShell
$timestamp = (Get-Date).ToString("yyyyMMdd_HHmmss")
Start-Transcript -Path "TeamsCompliancePolicy_$timestamp.log" -Append

Docs: Start-Transcript

Logs include all actions taken and whether each user or group assignment succeeded or failed — a must-have for compliance audits. You can optionally selectively log the successful and failed results for better post processing.

Verification and Propagation

After making changes, note that policy updates can take up to several hours to propagate across Microsoft Teams. Always perform verification tests — for instance, making a test call to confirm that the recording bot automatically joins.

On this page

↑ Back to top

Discover more from Argus Archive

Subscribe now to keep reading and get access to the full archive.

Continue reading