Collaboration/Get-TeamsSecurityConfig.ps1

<#
.SYNOPSIS
    Collects Microsoft Teams security and meeting configuration settings.
.DESCRIPTION
    Queries Microsoft Graph for Teams security-relevant settings including meeting
    policies, external access, messaging policies, and third-party app restrictions.
    Returns a structured inventory of settings with current values and CIS benchmark
    recommendations.

    Requires the following Graph API permissions:
    TeamSettings.Read.All, TeamworkAppSettings.Read.All
.PARAMETER OutputPath
    Optional path to export results as CSV. If not specified, results are returned to the pipeline.
.EXAMPLE
    PS> . .\Common\Connect-Service.ps1
    PS> Connect-Service -Service Graph -Scopes 'TeamSettings.Read.All','TeamworkAppSettings.Read.All'
    PS> .\Collaboration\Get-TeamsSecurityConfig.ps1

    Displays Teams security configuration settings.
.NOTES
    Author: Daren9m
    Settings checked are aligned with CIS Microsoft 365 Foundations Benchmark v6.0.1 recommendations.
#>

[CmdletBinding()]
param(
    [Parameter()]
    [ValidateNotNullOrEmpty()]
    [string]$OutputPath
)
. (Join-Path -Path $PSScriptRoot -ChildPath '../Common/Invoke-SafeGraphRequest.ps1')


# Continue on errors: non-critical checks should not block remaining assessments.
$ErrorActionPreference = 'Continue'

# Verify Graph connection
if (-not (Assert-GraphConnection)) { return }
$context = Get-MgContext

# Detect app-only auth — Teams Graph APIs (/v1.0/teamwork/*) do not support
# application-only context and return HTTP 412 "not supported in application-only context".
$isAppOnly = $context.AuthType -eq 'AppOnly' -or (-not $context.Account -and $context.AppName)
if ($isAppOnly) {
    Write-Warning "Teams Graph APIs do not support app-only (certificate) authentication. Teams security checks require delegated (interactive) auth. Skipping Teams collector."
    Write-Output @()
    return
}

# Detect whether the tenant has any Teams-capable licenses.
# If no Teams service plans are assigned, the /teamwork/* Graph endpoints return
# 400/404 errors, producing misleading warnings in the assessment log.
try {
    $subscribedSkus = Get-MgSubscribedSku -ErrorAction Stop
    # Service plan IDs from Microsoft's licensing-service-plan-reference. Sovereign
    # clouds ship Teams under cloud-specific plans — checking only TEAMS1 false-negatives
    # on every government tenant (observed live on a GCC High G5 tenant, #940).
    $teamsServicePlanIds = @(
        '57ff2da0-773e-42df-b2af-ffb7a2317929'  # TEAMS1 (commercial)
        '304767db-7d23-49e8-a945-4a7eb65f9f28'  # TEAMS_GOV (GCC)
        '9953b155-8aef-4c56-92f3-72b0487fce41'  # TEAMS_AR_GCCHIGH (GCC High)
        'fd500458-c24c-478e-856c-a6067a8376cd'  # TEAMS_AR_DOD (DoD)
    )
    $hasTeams = $false
    foreach ($sku in $subscribedSkus) {
        if ($sku.ConsumedUnits -gt 0) {
            foreach ($sp in $sku.ServicePlans) {
                if ($sp.ServicePlanId -in $teamsServicePlanIds -and $sp.ProvisioningStatus -ne 'Disabled') {
                    $hasTeams = $true
                    break
                }
            }
        }
        if ($hasTeams) { break }
    }
    if (-not $hasTeams) {
        Write-Warning "No Teams licenses detected in this tenant. Skipping Teams security checks to avoid false errors."
        Write-Output @()
        return
    }
}
catch {
    # If we can't check licenses, proceed with Teams checks and let them fail naturally
    Write-Warning "Could not verify Teams licensing: $($_.Exception.Message). Proceeding with Teams checks."
}

# Load shared security-config helpers
$_scriptDir = if ($MyInvocation.MyCommand.Path) { Split-Path -Parent $MyInvocation.MyCommand.Path } else { $PSScriptRoot }
. (Join-Path -Path $_scriptDir -ChildPath '..\Common\SecurityConfigHelper.ps1')

# Initialize-SecurityConfig records the active context; the shared Add-Setting from
# SecurityConfigHelper.ps1 reads it, so no local wrapper is needed (#958).
$ctx = Initialize-SecurityConfig
$settings = $ctx.Settings

# ------------------------------------------------------------------
# 1. Teams Client Configuration (external access)
# ------------------------------------------------------------------
try {
    Write-Verbose "Checking Teams external access settings..."
    $graphParams = @{
        Method      = 'GET'
        Uri         = '/v1.0/teamwork/teamsAppSettings'
        ErrorAction = 'Stop'
    }
    $teamsSettings = Invoke-SafeGraphRequest @graphParams

    $isSideloadingAllowed = $teamsSettings['isChatResourceSpecificConsentEnabled']
    $settingParams = @{
        Category         = 'Teams Apps'
        Setting          = 'Chat Resource-Specific Consent'
        CurrentValue     = "$isSideloadingAllowed"
        RecommendedValue = 'False'
        Status           = if (-not $isSideloadingAllowed) { 'Pass' } else { 'Review' }
        CheckId          = 'TEAMS-APPS-001'
        Remediation      = 'Run: Set-CsTeamsAppPermissionPolicy -DefaultCatalogAppsType AllowedAppList. Teams admin center > Teams apps > Permission policies.'
    }
    Add-Setting @settingParams
}
catch {
    Write-Warning "Could not retrieve Teams app settings: $_"
}

# ------------------------------------------------------------------
# 1b. Teams Client Configuration — unmanaged users (CIS 8.2.2, 8.2.3)
# ------------------------------------------------------------------
try {
    Write-Verbose "Checking Teams client configuration for unmanaged users..."
    $graphParams = @{
        Method      = 'GET'
        Uri         = '/beta/teamwork/teamsClientConfiguration'
        ErrorAction = 'Stop'
    }
    $teamsClientConfig = Invoke-SafeGraphRequest @graphParams

    if ($teamsClientConfig) {
        $allowConsumer = $teamsClientConfig['allowTeamsConsumer']
        $allowConsumerInbound = $teamsClientConfig['allowTeamsConsumerInbound']

        $settingParams = @{
            Category         = 'External Access'
            Setting          = 'Communication with Unmanaged Teams Users'
            CurrentValue     = "$allowConsumer"
            RecommendedValue = 'False'
            Status           = if (-not $allowConsumer) { 'Pass' } else { 'Fail' }
            CheckId          = 'TEAMS-EXTACCESS-001'
            Remediation      = 'Run: Set-CsTenantFederationConfiguration -AllowTeamsConsumer $false. Teams admin center > Users > External access > Teams accounts not managed by an organization > Off.'
        }
        Add-Setting @settingParams

        $settingParams = @{
            Category         = 'External Access'
            Setting          = 'External Unmanaged Users Can Initiate Conversations'
            CurrentValue     = "$allowConsumerInbound"
            RecommendedValue = 'False'
            Status           = if (-not $allowConsumerInbound) { 'Pass' } else { 'Fail' }
            CheckId          = 'TEAMS-EXTACCESS-002'
            Remediation      = 'Run: Set-CsTenantFederationConfiguration -AllowTeamsConsumerInbound $false. Teams admin center > Users > External access > External users can initiate conversations > Off.'
        }
        Add-Setting @settingParams

        # CIS 8.1.1 — Third-party cloud storage restricted
        $cloudStorageKeys = @('allowDropBox', 'allowBox', 'allowGoogleDrive', 'allowShareFile', 'allowEgnyte')
        $enabledStores = @()
        foreach ($key in $cloudStorageKeys) {
            if ($teamsClientConfig.ContainsKey($key) -and $teamsClientConfig[$key]) {
                $enabledStores += $key -replace '^allow', ''
            }
        }
        $cloudStorageStatus = if ($enabledStores.Count -eq 0) { 'Pass' } else { 'Fail' }
        $settingParams = @{
            Category         = 'Client Configuration'
            Setting          = 'Third-Party Cloud Storage'
            CurrentValue     = if ($enabledStores.Count -eq 0) { 'All disabled' } else { "Enabled: $($enabledStores -join ', ')" }
            RecommendedValue = 'All disabled'
            Status           = $cloudStorageStatus
            CheckId          = 'TEAMS-CLIENT-001'
            Remediation      = 'Run: Set-CsTeamsClientConfiguration -AllowDropBox $false -AllowBox $false -AllowGoogleDrive $false -AllowShareFile $false -AllowEgnyte $false. Teams admin center > Messaging policies > Manage third-party storage.'
        }
        Add-Setting @settingParams

        # CIS 8.1.2 — Channel email disabled
        $allowEmail = $teamsClientConfig['allowEmailIntoChannel']
        $settingParams = @{
            Category         = 'Client Configuration'
            Setting          = 'Email Into Channel'
            CurrentValue     = "$allowEmail"
            RecommendedValue = 'False'
            Status           = if (-not $allowEmail) { 'Pass' } else { 'Fail' }
            CheckId          = 'TEAMS-CLIENT-002'
            Remediation      = 'Run: Set-CsTeamsClientConfiguration -AllowEmailIntoChannel $false. Teams admin center > Teams settings > Email integration > Users can send emails to a channel email address > Off.'
        }
        Add-Setting @settingParams

        # CIS 8.2.1 — External domain access restricted
        $allowFederated = $teamsClientConfig['allowFederatedUsers']
        $allowedDomains = $teamsClientConfig['allowedDomains']
        $domainRestricted = (-not $allowFederated) -or ($allowedDomains -and $allowedDomains.Count -gt 0)
        $settingParams = @{
            Category         = 'External Access'
            Setting          = 'External Domain Access'
            CurrentValue     = if (-not $allowFederated) { 'Disabled' } elseif ($allowedDomains -and $allowedDomains.Count -gt 0) { "Restricted to $($allowedDomains.Count) domains" } else { 'Open to all domains' }
            RecommendedValue = 'Disabled or restricted to specific domains'
            Status           = if ($domainRestricted) { 'Pass' } else { 'Fail' }
            CheckId          = 'TEAMS-EXTACCESS-003'
            Remediation      = 'Run: Set-CsTenantFederationConfiguration -AllowFederatedUsers $false (or restrict with -AllowedDomains). Teams admin center > Users > External access > Choose which external domains your users have access to > Allow only specific external domains.'
        }
        Add-Setting @settingParams

        # CIS 8.2.4 — Skype for Business interop disabled
        $allowPublicUsers = $teamsClientConfig['allowPublicUsers']
        $settingParams = @{
            Category         = 'External Access'
            Setting          = 'Skype for Business/Consumer Interop'
            CurrentValue     = "$allowPublicUsers"
            RecommendedValue = 'False'
            Status           = if (-not $allowPublicUsers) { 'Pass' } else { 'Fail' }
            CheckId          = 'TEAMS-EXTACCESS-004'
            Remediation      = 'Run: Set-CsTenantFederationConfiguration -AllowPublicUsers $false. Teams admin center > Users > External access > Skype users > Off.'
        }
        Add-Setting @settingParams
    }
}
catch {
    Write-Warning "Teams client configuration endpoint unavailable: $($_.Exception.Message)"
    # The /beta/teamwork/teamsClientConfiguration endpoint is beta-only with no v1.0
    # equivalent and is not served in sovereign clouds (GCC High/DoD), where it 400s.
    # Emit Skipped for the dependent checks so they surface in the report's
    # not-assessed group instead of silently vanishing (#940).
    $clientConfigGapChecks = @(
        @{ CheckId = 'TEAMS-EXTACCESS-001'; Category = 'External Access';      Setting = 'Communication with Unmanaged Teams Users' }
        @{ CheckId = 'TEAMS-EXTACCESS-002'; Category = 'External Access';      Setting = 'External Unmanaged Users Can Initiate Conversations' }
        @{ CheckId = 'TEAMS-CLIENT-001';    Category = 'Client Configuration'; Setting = 'Third-Party Cloud Storage' }
        @{ CheckId = 'TEAMS-CLIENT-002';    Category = 'Client Configuration'; Setting = 'Email Into Channel' }
        @{ CheckId = 'TEAMS-EXTACCESS-003'; Category = 'External Access';      Setting = 'External Domain Access' }
        @{ CheckId = 'TEAMS-EXTACCESS-004'; Category = 'External Access';      Setting = 'Skype for Business/Consumer Interop' }
    )
    foreach ($gap in $clientConfigGapChecks) {
        $skipParams = @{
            Category         = $gap.Category
            Setting          = $gap.Setting
            CurrentValue     = 'Not assessed'
            RecommendedValue = 'n/a'
            Status           = 'Skipped'
            CheckId          = $gap.CheckId
            Remediation      = 'The Teams client configuration endpoint (/beta/teamwork/teamsClientConfiguration) is not available in this cloud environment. Sovereign clouds (GCC High/DoD) do not serve it and there is no v1.0 equivalent. Verify manually in Teams admin center > Users > External access.'
        }
        Add-Setting @skipParams
    }
}

# ------------------------------------------------------------------
# 2. Teams Meeting Policies (via beta API)
# ------------------------------------------------------------------
try {
    Write-Verbose "Checking Teams meeting policy..."
    $graphParams = @{
        Method      = 'GET'
        Uri         = '/beta/teamwork/teamsMeetingPolicy'
        ErrorAction = 'Stop'
    }
    $meetingPolicy = Invoke-SafeGraphRequest @graphParams

    if ($meetingPolicy) {
        $anonymousJoin = $meetingPolicy['allowAnonymousUsersToJoinMeeting']
        $settingParams = @{
            Category         = 'Meeting Policy'
            Setting          = 'Anonymous Users Can Join Meeting'
            CurrentValue     = "$anonymousJoin"
            RecommendedValue = 'False'
            Status           = if (-not $anonymousJoin) { 'Pass' } else { 'Fail' }
            CheckId          = 'TEAMS-MEETING-001'
            Remediation      = 'Run: Set-CsTeamsMeetingPolicy -Identity Global -AllowAnonymousUsersToJoinMeeting $false. Teams admin center > Meetings > Meeting policies > Global > Anonymous users can join a meeting > Off.'
        }
        Add-Setting @settingParams

        # Anonymous/dial-in can't start meeting (CIS 8.5.2)
        $anonStart = $meetingPolicy['allowAnonymousUsersToStartMeeting']
        $settingParams = @{
            Category         = 'Meeting Policy'
            Setting          = 'Anonymous Users Can Start Meeting'
            CurrentValue     = "$anonStart"
            RecommendedValue = 'False'
            Status           = if (-not $anonStart) { 'Pass' } else { 'Fail' }
            CheckId          = 'TEAMS-MEETING-002'
            Remediation      = 'Run: Set-CsTeamsMeetingPolicy -Identity Global -AllowAnonymousUsersToStartMeeting $false. Teams admin center > Meetings > Meeting policies > Global > Anonymous users can start a meeting > Off.'
        }
        Add-Setting @settingParams

        # Auto-admitted users / lobby bypass (CIS 8.5.3)
        $autoAdmit = $meetingPolicy['autoAdmittedUsers']
        $autoAdmitPass = $autoAdmit -eq 'EveryoneInCompanyExcludingGuests' -or $autoAdmit -eq 'EveryoneInSameAndFederatedCompany' -or $autoAdmit -eq 'OrganizerOnly' -or $autoAdmit -eq 'InvitedUsers'
        $settingParams = @{
            Category         = 'Meeting Policy'
            Setting          = 'Auto-Admitted Users (Lobby Bypass)'
            CurrentValue     = "$autoAdmit"
            RecommendedValue = 'EveryoneInCompanyExcludingGuests or stricter'
            Status           = if ($autoAdmitPass) { 'Pass' } else { 'Fail' }
            CheckId          = 'TEAMS-MEETING-003'
            Remediation      = 'Run: Set-CsTeamsMeetingPolicy -Identity Global -AutoAdmittedUsers EveryoneInCompanyExcludingGuests. Teams admin center > Meetings > Meeting policies > Global > Who can bypass the lobby > People in my org.'
        }
        Add-Setting @settingParams

        # Dial-in users can't bypass lobby (CIS 8.5.4)
        $pstnBypass = $meetingPolicy['allowPSTNUsersToBypassLobby']
        $settingParams = @{
            Category         = 'Meeting Policy'
            Setting          = 'Dial-in Users Bypass Lobby'
            CurrentValue     = "$pstnBypass"
            RecommendedValue = 'False'
            Status           = if (-not $pstnBypass) { 'Pass' } else { 'Fail' }
            CheckId          = 'TEAMS-MEETING-004'
            Remediation      = 'Run: Set-CsTeamsMeetingPolicy -Identity Global -AllowPSTNUsersToBypassLobby $false. Teams admin center > Meetings > Meeting policies > Global > Dial-in users can bypass the lobby > Off.'
        }
        Add-Setting @settingParams

        # External participants can't give/request control (CIS 8.5.7)
        $extControl = $meetingPolicy['allowExternalParticipantGiveRequestControl']
        $settingParams = @{
            Category         = 'Meeting Policy'
            Setting          = 'External Participants Can Give/Request Control'
            CurrentValue     = "$extControl"
            RecommendedValue = 'False'
            Status           = if (-not $extControl) { 'Pass' } else { 'Warning' }
            CheckId          = 'TEAMS-MEETING-005'
            Remediation      = 'Run: Set-CsTeamsMeetingPolicy -Identity Global -AllowExternalParticipantGiveRequestControl $false. Teams admin center > Meetings > Meeting policies > Global > External participants can give or request control > Off.'
        }
        Add-Setting @settingParams

        # CIS 8.5.5 — Anonymous meeting chat blocked
        $meetingChat = $meetingPolicy['meetingChatEnabledType']
        $chatPass = $meetingChat -ne 'Enabled'
        $settingParams = @{
            Category         = 'Meeting Policy'
            Setting          = 'Meeting Chat for Anonymous Users'
            CurrentValue     = "$meetingChat"
            RecommendedValue = 'Disabled or EnabledExceptAnonymous'
            Status           = if ($chatPass) { 'Pass' } else { 'Fail' }
            CheckId          = 'TEAMS-MEETING-006'
            Remediation      = 'Run: Set-CsTeamsMeetingPolicy -Identity Global -MeetingChatEnabledType EnabledExceptAnonymous. Teams admin center > Meetings > Meeting policies > Global > Meeting chat > On for everyone except anonymous users.'
        }
        Add-Setting @settingParams

        # CIS 8.5.6 — Only organizers can present
        $presenterRole = $meetingPolicy['designatedPresenterRoleMode']
        $presenterPass = $presenterRole -eq 'OrganizerOnlyUserOverride'
        $settingParams = @{
            Category         = 'Meeting Policy'
            Setting          = 'Default Presenter Role'
            CurrentValue     = "$presenterRole"
            RecommendedValue = 'OrganizerOnlyUserOverride'
            Status           = if ($presenterPass) { 'Pass' } else { 'Fail' }
            CheckId          = 'TEAMS-MEETING-007'
            Remediation      = 'Run: Set-CsTeamsMeetingPolicy -Identity Global -DesignatedPresenterRoleMode OrganizerOnlyUserOverride. Teams admin center > Meetings > Meeting policies > Global > Who can present > Only organizers.'
        }
        Add-Setting @settingParams

        # CIS 8.5.8 — External meeting chat off
        $extMeetingChat = $meetingPolicy['allowExternalNonTrustedMeetingChat']
        $settingParams = @{
            Category         = 'Meeting Policy'
            Setting          = 'External Meeting Chat (Non-Trusted)'
            CurrentValue     = "$extMeetingChat"
            RecommendedValue = 'False'
            Status           = if (-not $extMeetingChat) { 'Pass' } else { 'Fail' }
            CheckId          = 'TEAMS-MEETING-008'
            Remediation      = 'Run: Set-CsTeamsMeetingPolicy -Identity Global -AllowExternalNonTrustedMeetingChat $false. Teams admin center > Meetings > Meeting policies > Global > External meeting chat > Off.'
        }
        Add-Setting @settingParams

        # CIS 8.5.9 — Cloud recording off by default
        $cloudRecording = $meetingPolicy['allowCloudRecording']
        $settingParams = @{
            Category         = 'Meeting Policy'
            Setting          = 'Cloud Recording'
            CurrentValue     = "$cloudRecording"
            RecommendedValue = 'False'
            Status           = if (-not $cloudRecording) { 'Pass' } else { 'Fail' }
            CheckId          = 'TEAMS-MEETING-009'
            Remediation      = 'Run: Set-CsTeamsMeetingPolicy -Identity Global -AllowCloudRecording $false. Teams admin center > Meetings > Meeting policies > Global > Cloud recording > Off.'
        }
        Add-Setting @settingParams
    }
}
catch {
    Write-Warning "Teams meeting policy endpoint unavailable: $($_.Exception.Message)"
    # /beta/teamwork/teamsMeetingPolicy is beta-only with no v1.0 equivalent and is
    # not served in sovereign clouds (GCC High/DoD), where it 400s. Emit Skipped for
    # the dependent checks so they surface in the report's not-assessed group (#940).
    $meetingPolicyGapChecks = @(
        @{ CheckId = 'TEAMS-MEETING-001'; Setting = 'Anonymous Users Can Join Meeting' }
        @{ CheckId = 'TEAMS-MEETING-002'; Setting = 'Anonymous Users Can Start Meeting' }
        @{ CheckId = 'TEAMS-MEETING-003'; Setting = 'Auto-Admitted Users (Lobby Bypass)' }
        @{ CheckId = 'TEAMS-MEETING-004'; Setting = 'Dial-in Users Bypass Lobby' }
        @{ CheckId = 'TEAMS-MEETING-005'; Setting = 'External Participants Can Give/Request Control' }
        @{ CheckId = 'TEAMS-MEETING-006'; Setting = 'Meeting Chat for Anonymous Users' }
        @{ CheckId = 'TEAMS-MEETING-007'; Setting = 'Default Presenter Role' }
        @{ CheckId = 'TEAMS-MEETING-008'; Setting = 'External Meeting Chat (Non-Trusted)' }
        @{ CheckId = 'TEAMS-MEETING-009'; Setting = 'Cloud Recording' }
    )
    foreach ($gap in $meetingPolicyGapChecks) {
        $skipParams = @{
            Category         = 'Meeting Policy'
            Setting          = $gap.Setting
            CurrentValue     = 'Not assessed'
            RecommendedValue = 'n/a'
            Status           = 'Skipped'
            CheckId          = $gap.CheckId
            Remediation      = 'The Teams meeting policy endpoint (/beta/teamwork/teamsMeetingPolicy) is not available in this cloud environment. Sovereign clouds (GCC High/DoD) do not serve it and there is no v1.0 equivalent. Verify manually in Teams admin center > Meetings > Meeting policies.'
        }
        Add-Setting @skipParams
    }
}

# ------------------------------------------------------------------
# 3. Teams Settings (tenant-level)
# ------------------------------------------------------------------
try {
    Write-Verbose "Checking tenant-level Teams settings..."
    $graphParams = @{
        Method      = 'GET'
        Uri         = '/v1.0/teamwork'
        ErrorAction = 'Stop'
    }
    $teamSettings = Invoke-SafeGraphRequest @graphParams

    if ($teamSettings) {
        $settingParams = @{
            Category         = 'Teams Settings'
            Setting          = 'Teams Workload Active'
            CurrentValue     = 'Active'
            RecommendedValue = 'Active'
            Status           = 'Info'
            CheckId          = 'TEAMS-INFO-001'
            Remediation      = 'Informational — confirms Teams service connectivity.'
        }
        Add-Setting @settingParams
    }
}
catch {
    Write-Warning "Teams settings endpoint unavailable: $($_.Exception.Message)"
}

# ------------------------------------------------------------------
# Teams App Permission Policies (CIS 8.4.1 - Review)
# ------------------------------------------------------------------
$settingParams = @{
    Category         = 'Teams Apps'
    Setting          = 'Third-Party App Permission Policies'
    CurrentValue     = 'Cannot be fully checked via API'
    RecommendedValue = 'Block third-party apps or restrict to approved list'
    Status           = 'Review'
    CheckId          = 'TEAMS-APPS-002'
    Remediation      = 'Teams admin center > Teams apps > Permission policies > Org-wide app settings > Third-party apps > Off (or restrict to approved apps).'
}
Add-Setting @settingParams

# ------------------------------------------------------------------
# Teams Report a Security Concern (CIS 8.6.1 - Review)
# ------------------------------------------------------------------
$settingParams = @{
    Category         = 'Teams Settings'
    Setting          = 'Report a Security Concern Enabled'
    CurrentValue     = 'Cannot be checked via API'
    RecommendedValue = 'Enabled in messaging policies'
    Status           = 'Review'
    CheckId          = 'TEAMS-REPORTING-001'
    Remediation      = 'Teams admin center > Messaging policies > Global (Org-wide default) > Report a security concern > On.'
}
Add-Setting @settingParams

# ------------------------------------------------------------------
# Output
# ------------------------------------------------------------------
Export-SecurityConfigReport -Settings $settings -OutputPath $OutputPath -ServiceLabel 'Teams'