UCLobbyTeams.psm1

#Region '.\Private\Convert-UcTeamsDeviceType.ps1' 0
function Convert-UcTeamsDeviceType {
    param (
        [string]$DeviceType
    )
    switch ($DeviceType) {
        "ipPhone" { return "Phone" }
        "lowCostPhone" { return "Phone" }
        "teamsRoom" { return "MTR Windows" }
        "collaborationBar" { return "MTR Android" }
        "surfaceHub" { return "Surface Hub" }
        "teamsDisplay" { return "Display" }
        "touchConsole" { return "Touch Console (MTRA)" }
        "teamsPanel" { return "Panel" }
        "sip" { return "SIP Phone" }
        Default { return $DeviceType}
    }
}
#EndRegion '.\Private\Convert-UcTeamsDeviceType.ps1' 18
#Region '.\Public\Get-UcArch.ps1' 0
<#
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.
#>


<#
.SYNOPSIS
Funcion to get the Architecture from .exe file
 
.DESCRIPTION
Based on PowerShell script Get-ExecutableType.ps1 by David Wyatt, please check the complete script in:
 
Identify 16-bit, 32-bit and 64-bit executables with PowerShell
https://gallery.technet.microsoft.com/scriptcenter/Identify-16-bit-32-bit-and-522eae75
 
.PARAMETER FilePath
Specifies the executable full file path.
 
.EXAMPLE
PS> Get-UcArch -FilePath C:\temp\example.exe
#>

Function Get-UcArch([string]$FilePath) {
    try {
        $stream = New-Object System.IO.FileStream(
            $FilePath,
            [System.IO.FileMode]::Open,
            [System.IO.FileAccess]::Read,
            [System.IO.FileShare]::Read )
        $exeType = 'Unknown'
        $bytes = New-Object byte[](4)
        if ($stream.Seek(0x3C, [System.IO.SeekOrigin]::Begin) -eq 0x3C -and $stream.Read($bytes, 0, 4) -eq 4) {
            if (-not [System.BitConverter]::IsLittleEndian) { [Array]::Reverse($bytes, 0, 4) }
            $peHeaderOffset = [System.BitConverter]::ToUInt32($bytes, 0)

            if ($stream.Length -ge $peHeaderOffset + 6 -and
                $stream.Seek($peHeaderOffset, [System.IO.SeekOrigin]::Begin) -eq $peHeaderOffset -and
                $stream.Read($bytes, 0, 4) -eq 4 -and
                $bytes[0] -eq 0x50 -and $bytes[1] -eq 0x45 -and $bytes[2] -eq 0 -and $bytes[3] -eq 0) {
                $exeType = 'Unknown'
                if ($stream.Read($bytes, 0, 2) -eq 2) {
                    if (-not [System.BitConverter]::IsLittleEndian) { [Array]::Reverse($bytes, 0, 2) }
                    $machineType = [System.BitConverter]::ToUInt16($bytes, 0)
                    switch ($machineType) {
                        0x014C { $exeType = 'x86' }
                        0x8664 { $exeType = 'x64' }
                    }
                }
            }
        }
        return $exeType
    }
    catch {
        return "Unknown"
    }
    finally {
        if ($null -ne $stream) { $stream.Dispose() }
    }
}
#EndRegion '.\Public\Get-UcArch.ps1' 64
#Region '.\Public\Get-UcM365Domains.ps1' 0
<#
.SYNOPSIS
Get Microsoft 365 Domains from a Tenant
 
.DESCRIPTION
This function returns a list of domains that are associated with a Microsoft 365 Tenant.
 
.PARAMETER Domain
Specifies a domain registered with Microsoft 365
 
.EXAMPLE
PS> Get-UcM365Domains -Domain uclobby.com
#>

Function Get-UcM365Domains {
    Param(
        [Parameter(Mandatory = $true)]
        [string]$Domain
    )
    $regex = "^(.*@)(.*[.].*)$"
    $outDomains = [System.Collections.ArrayList]::new()
    try {
        $AllowedAudiences = Invoke-WebRequest -Uri ("https://accounts.accesscontrol.windows.net/" + $Domain + "/metadata/json/1") | ConvertFrom-Json | Select-Object -ExpandProperty allowedAudiences
        foreach ($AllowedAudience in $AllowedAudiences) {
            $temp = [regex]::Match($AllowedAudience , $regex).captures.groups
            if ($temp.count -ge 2) {
                $tempObj = New-Object -TypeName PSObject -Property @{
                    Name = $temp[2].value
                }
                $outDomains.Add($tempObj) | Out-Null
            }
        }
    }
    catch [System.Net.WebException] {
        if ($PSItem.Exception.Message -eq "The remote server returned an error: (400) Bad Request.") {
            Write-Warning "The domain $Domain is not part of a Microsoft 365 Tenant."
        }
        else {
            Write-Warning $PSItem.Exception.Message
        }
    }
    catch {
        Write-Warning "Unknown error while checking domain: $Domain"
    }
    return $outDomains
}
#EndRegion '.\Public\Get-UcM365Domains.ps1' 46
#Region '.\Public\Get-UcM365TenantId.ps1' 0
<#
.SYNOPSIS
Get Microsoft 365 Tenant Id
 
.DESCRIPTION
This function returns the Tenant ID associated with a domain that is part of a Microsoft 365 Tenant.
 
.PARAMETER Domain
Specifies a domain registered with Microsoft 365
 
.EXAMPLE
PS> Get-UcM365TenantId -Domain uclobby.com
#>

Function Get-UcM365TenantId {
    Param(
        [Parameter(Mandatory = $true)]
        [string]$Domain
    )
    $regexTenantID = "^(.*@)(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})$"
    $regexOnMicrosoftDomain = "^(.*@)(?!.*mail)(.*.onmicrosoft.com)$"

    try {
        $AllowedAudiences = Invoke-WebRequest -Uri ("https://accounts.accesscontrol.windows.net/" + $Domain + "/metadata/json/1") | ConvertFrom-Json | Select-Object -ExpandProperty allowedAudiences
    }
    catch [System.Net.WebException] {
        if ($PSItem.Exception.Message -eq "The remote server returned an error: (400) Bad Request.") {
            Write-Warning "The domain $Domain is not part of a Microsoft 365 Tenant."
        }
        else {
            Write-Warning $PSItem.Exception.Message
        }
    }
    catch {
        Write-Warning "Unknown error while checking domain: $Domain"
    }
    $output = [System.Collections.ArrayList]::new()
    $TenantID = ""
    foreach ($AllowedAudience in $AllowedAudiences) {
        $tempTID = [regex]::Match($AllowedAudience , $regexTenantID).captures.groups
        $tempID = [regex]::Match($AllowedAudience , $regexOnMicrosoftDomain).captures.groups
        if ($tempTID.count -ge 2) {
            $TenantID = $tempTID[2].value 
        }
        if ($tempID.count -ge 2) {
            $OnMicrosoftDomain = $tempID[2].value
        }
        if($TenantID -and $OnMicrosoftDomain){
            $M365TidPSObj = New-Object -TypeName PSObject -Property @{ TenantID = $TenantID
                OnMicrosoftDomain = $OnMicrosoftDomain}
            $M365TidPSObj.PSObject.TypeNames.Insert(0, 'M365TenantId')
            [void]$output.Add($M365TidPSObj)
        }
    }
    return $output
}
#EndRegion '.\Public\Get-UcM365TenantId.ps1' 56
#Region '.\Public\Get-UcTeamsDevice.ps1' 0
<#
.SYNOPSIS
Get Microsoft Teams Devices information
 
.DESCRIPTION
This function fetch Teams Devices provisioned in a M365 Tenant using MS Graph.
  
 
Contributors: David Paulino, Silvio Schanz, Gonçalo Sepulveda and Bryan Kendrick
 
Requirements: Microsoft Graph PowerShell Module (Install-Module Microsoft.Graph)
                Microsoft Graph Scopes:
                        "TeamworkDevice.Read.All"
                        "User.Read.All"
 
.PARAMETER Filter
Specifies a filter, valid options:
    Phone - Teams Native Phones
    MTR - Microsoft Teams Rooms running Windows or Android
    MTRW - Microsoft Teams Room Running Windows
    MTRA - Microsoft Teams Room Running Android
    SurfaceHub - Surface Hub
    Display - Microsoft Teams Displays
    Panel - Microsoft Teams Panels
 
.PARAMETER Detailed
When present it will get detailed information from Teams Devices
 
.EXAMPLE
PS> Get-UcTeamsDevice
 
.EXAMPLE
PS> Get-UcTeamsDevice -Filter MTR
 
.EXAMPLE
PS> Get-UcTeamsDevice -Detailed
 
#>


$GraphURI_BetaAPIBatch = "https://graph.microsoft.com/beta/`$batch"

Function Get-UcTeamsDevice {
    Param(
        [ValidateSet("Phone","MTR","MTRA","MTRW","SurfaceHub","Display","Panel","SIPPhone")]
        [string]$Filter,
        [switch]$Detailed
    )    
    $outTeamsDevices = [System.Collections.ArrayList]::new()
    
    #Checking if we have the required scopes.
    $scopes = (Get-MgContext).Scopes
    if (!($scopes) -or !(( "TeamworkDevice.Read.All" -in $scopes ) -and ("User.Read.All" -in $scopes))) {
        Connect-MgGraph -Scopes "TeamworkDevice.Read.All", "User.Read.All"
    }

    $graphRequests =  [System.Collections.ArrayList]::new()
    switch ($filter) {
        "Phone" { 
            $gRequestTmp = New-Object -TypeName PSObject -Property @{
                id = "ipPhone"
                method = "GET"
                url = "/teamwork/devices/?`$filter=deviceType eq 'ipPhone'"
            }
            $graphRequests.Add($gRequestTmp) | Out-Null
            $gRequestTmp = New-Object -TypeName PSObject -Property @{
                id = "lowCostPhone"
                method = "GET"
                url = "/teamwork/devices/?`$filter=deviceType eq 'lowCostPhone'"
            }
            $graphRequests.Add($gRequestTmp) | Out-Null
        }
        "MTR" {
            $gRequestTmp = New-Object -TypeName PSObject -Property @{
                id = "teamsRoom"
                method = "GET"
                url = "/teamwork/devices/?`$filter=deviceType eq 'teamsRoom'"
            }
            $graphRequests.Add($gRequestTmp) | Out-Null
            $gRequestTmp = New-Object -TypeName PSObject -Property @{
                id = "collaborationBar"
                method = "GET"
                url = "/teamwork/devices/?`$filter=deviceType eq 'collaborationBar'"
            }
            $graphRequests.Add($gRequestTmp) | Out-Null
            $gRequestTmp = New-Object -TypeName PSObject -Property @{
                id = "touchConsole"
                method = "GET"
                url = "/teamwork/devices/?`$filter=deviceType eq 'touchConsole'"
            }
            $graphRequests.Add($gRequestTmp) | Out-Null
        }
        "MTRW"{
            $gRequestTmp = New-Object -TypeName PSObject -Property @{
                id = "teamsRoom"
                method = "GET"
                url = "/teamwork/devices/?`$filter=deviceType eq 'teamsRoom'"
            }
            $graphRequests.Add($gRequestTmp) | Out-Null
        }
        "MTRA"{            
            $gRequestTmp = New-Object -TypeName PSObject -Property @{
                id = "collaborationBar"
                method = "GET"
                url = "/teamwork/devices/?`$filter=deviceType eq 'collaborationBar'"
            }
            $graphRequests.Add($gRequestTmp) | Out-Null
            $gRequestTmp = New-Object -TypeName PSObject -Property @{
                id = "touchConsole"
                method = "GET"
                url = "/teamwork/devices/?`$filter=deviceType eq 'touchConsole'"
            }
            $graphRequests.Add($gRequestTmp) | Out-Null
        }
        "SurfaceHub" {
            $gRequestTmp = New-Object -TypeName PSObject -Property @{
                id = "surfaceHub"
                method = "GET"
                url = "/teamwork/devices/?`$filter=deviceType eq 'surfaceHub'"
            }
            $graphRequests.Add($gRequestTmp) | Out-Null
        }
        "Display"{
            $gRequestTmp = New-Object -TypeName PSObject -Property @{
                id = "teamsDisplay"
                method = "GET"
                url = "/teamwork/devices/?`$filter=deviceType eq 'teamsDisplay'"
            }
            $graphRequests.Add($gRequestTmp) | Out-Null
        }
        "Panel" {
            $gRequestTmp = New-Object -TypeName PSObject -Property @{
                id = "teamsPanel"
                method = "GET"
                url = "/teamwork/devices/?`$filter=deviceType eq 'teamsPanel'"
            }
            $graphRequests.Add($gRequestTmp) | Out-Null
        }
        "SIPPhone" {
            $gRequestTmp = New-Object -TypeName PSObject -Property @{
                id = "sip"
                method = "GET"
                url = "/teamwork/devices/?`$filter=deviceType eq 'sip'"
            }
            $graphRequests.Add($gRequestTmp) | Out-Null
        }
        Default {
            $gRequestTmp = New-Object -TypeName PSObject -Property @{
                id = 1
                method = "GET"
                url = "/teamwork/devices/"
            }
            $graphRequests.Add($gRequestTmp) | Out-Null
        }
    }
    
    #TO DO: Look for alternatives instead of doing this.
    if($graphRequests.Count -gt 1){
        $graphBody = ' { "requests": '+ ($graphRequests | ConvertTo-Json) + ' }' 
    } else {
        $graphBody = ' { "requests": ['+ ($graphRequests | ConvertTo-Json) + '] }' 
    }
    $graphResponses = (Invoke-MgGraphRequest -Method Post -Uri $GraphURI_BetaAPIBatch -Body $graphBody).responses
    
    #For performance is better to get all users in one graph request
    $graphResponseExtra =  [System.Collections.ArrayList]::new()
    for($j=0;$j -lt $graphResponses.length; $j++){
        $graphRequestsExtra =  [System.Collections.ArrayList]::new()

        $TeamsDeviceList = $graphResponses[$j].body.value
        $i = 1
        foreach($TeamsDevice in $TeamsDeviceList){
            $batchCount = [int](($TeamsDeviceList.length * 5)/20)+1
            Write-Progress -Activity "Teams Device List" -Status "Running batch $i of $batchCount"  -PercentComplete (($i / $batchCount) * 100)

            if(($graphRequestsExtra.id -notcontains $TeamsDevice.currentuser.id) -and ($null -ne $TeamsDevice.currentuser.id) -and ($graphResponseExtra.id -notcontains $TeamsDevice.currentuser.id)) {
                $gRequestTmp = New-Object -TypeName PSObject -Property @{
                    id =  $TeamsDevice.currentuser.id
                    method = "GET"
                    url = "/users/"+ $TeamsDevice.currentuser.id
                }
                $graphRequestsExtra.Add($gRequestTmp) | Out-Null
            }
            if($Detailed){
                $gRequestTmp = New-Object -TypeName PSObject -Property @{
                    id = $TeamsDevice.id+"-activity"
                    method = "GET"
                    url = "/teamwork/devices/"+$TeamsDevice.id+"/activity"
                }
                $graphRequestsExtra.Add($gRequestTmp) | Out-Null
                $gRequestTmp = New-Object -TypeName PSObject -Property @{
                    id = $TeamsDevice.id+"-configuration"
                    method = "GET"
                    url = "/teamwork/devices/"+$TeamsDevice.id+"/configuration"
                }
                $graphRequestsExtra.Add($gRequestTmp) | Out-Null
                $gRequestTmp = New-Object -TypeName PSObject -Property @{
                    id =$TeamsDevice.id+"-health"
                    method = "GET"
                    url = "/teamwork/devices/"+$TeamsDevice.id+"/health"
                }
                $graphRequestsExtra.Add($gRequestTmp) | Out-Null
                $gRequestTmp = New-Object -TypeName PSObject -Property @{
                    id = $TeamsDevice.id+"-operations"
                    method = "GET"
                    url = "/teamwork/devices/"+$TeamsDevice.id+"/operations"
                }
                $graphRequestsExtra.Add($gRequestTmp) | Out-Null
            } 

            #MS Graph is limited to 20 requests per batch, each device has 5 requests unless we already know the User UPN.
            if($graphRequestsExtra.Count -gt 15)  {
                $i++
                $graphBodyExtra = ' { "requests": '+ ($graphRequestsExtra  | ConvertTo-Json) + ' }' 
                $graphResponseExtra += (Invoke-MgGraphRequest -Method Post -Uri $GraphURI_BetaAPIBatch -Body $graphBodyExtra).responses
                $graphRequestsExtra =  [System.Collections.ArrayList]::new()
            }
        }
        #Checking if we have requests pending
        if ($graphRequestsExtra.Count -gt 0){
            Write-Progress -Activity "Teams Device List" -Status "Running batch $i of $batchCount"  -PercentComplete (($i / $batchCount) * 100)
            if($graphRequestsExtra.Count -gt 1){
                $graphBodyExtra = ' { "requests": '+ ($graphRequestsExtra | ConvertTo-Json) + ' }' 
            } else {
                $graphBodyExtra = ' { "requests": ['+ ($graphRequestsExtra | ConvertTo-Json) + '] }' 
            }
            $graphResponseExtra += (Invoke-MgGraphRequest -Method Post -Uri $GraphURI_BetaAPIBatch -Body $graphBodyExtra).responses
        }
    }
    for($j=0;$j -lt $graphResponses.length; $j++){
        if($graphResponses[$j].status -eq 200){
            $TeamsDeviceList = $graphResponses[$j].body.value
            
            foreach($TeamsDevice in $TeamsDeviceList){
                $userUPN = ($graphResponseExtra | Where-Object{$_.id -eq $TeamsDevice.currentuser.id}).body.userPrincipalName

                if($Detailed){
                    $TeamsDeviceActivity = ($graphResponseExtra | Where-Object{$_.id -eq ($TeamsDevice.id+"-activity")}).body
                    $TeamsDeviceConfiguration = ($graphResponseExtra | Where-Object{$_.id -eq ($TeamsDevice.id+"-configuration")}).body
                    $TeamsDeviceHealth = ($graphResponseExtra | Where-Object{$_.id -eq ($TeamsDevice.id+"-health")}).body
                    $TeamsDeviceOperations = ($graphResponseExtra | Where-Object{$_.id -eq ($TeamsDevice.id+"-operations")}).body.value

                    if($TeamsDeviceOperations.count -gt 0){
                        $LastHistoryAction = $TeamsDeviceOperations[0].operationType
                        $LastHistoryStatus = $TeamsDeviceOperations[0].status
                        $LastHistoryInitiatedBy = $TeamsDeviceOperations[0].createdBy.user.displayName
                        $LastHistoryModifiedDate = $TeamsDeviceOperations[0].lastActionDateTime
                        $LastHistoryErrorCode = $TeamsDeviceOperations[0].error.code
                        $LastHistoryErrorMessage = $TeamsDeviceOperations[0].error.message
                    } else {
                        $LastHistoryAction = ""
                        $LastHistoryStatus = ""
                        $LastHistoryInitiatedBy = ""
                        $LastHistoryModifiedDate = ""
                        $LastHistoryErrorCode = ""
                        $LastHistoryErrorMessage = ""
                    }
            
                    $TDObj = New-Object -TypeName PSObject -Property @{
                        UserDisplayName = $TeamsDevice.currentuser.displayName
                        UserUPN         = $userUPN 
                
                        DeviceType      = Convert-UcTeamsDeviceType $TeamsDevice.deviceType
                        Notes           = $TeamsDevice.notes
                        CompanyAssetTag = $TeamsDevice.companyAssetTag
            
                        Manufacturer    = $TeamsDevice.hardwaredetail.manufacturer
                        Model           = $TeamsDevice.hardwaredetail.model
                        SerialNumber    = $TeamsDevice.hardwaredetail.serialNumber 
                        MacAddresses    = $TeamsDevice.hardwaredetail.macAddresses
                                    
                        DeviceHealth    = $TeamsDevice.healthStatus
                        WhenCreated = $TeamsDevice.createdDateTime
                        WhenChanged = $TeamsDevice.lastModifiedDateTime
                        ChangedByUser = $TeamsDevice.lastModifiedBy.user.displayName
            
                        #Activity
                        ActivePeripherals = $TeamsDeviceActivity.activePeripherals
            
                        #Configuration
                        LastUpdate = $TeamsDeviceConfiguration.createdDateTime
            
                        DisplayConfiguration = $TeamsDeviceConfiguration.displayConfiguration
                        CameraConfiguration = $TeamsDeviceConfiguration.cameraConfiguration
                        SpeakerConfiguration = $TeamsDeviceConfiguration.speakerConfiguration
                        MicrophoneConfiguration = $TeamsDeviceConfiguration.microphoneConfiguration
                        TeamsClientConfiguration = $TeamsDeviceConfiguration.teamsClientConfiguration
                        HardwareConfiguration = $TeamsDeviceConfiguration.hardwareConfiguration
                        SystemConfiguration = $TeamsDeviceConfiguration.systemConfiguration
            
                        #Health
                        TeamsAdminAgentVersion = $TeamsDeviceHealth.softwareUpdateHealth.adminAgentSoftwareUpdateStatus.currentVersion
                        FirmwareVersion = $TeamsDeviceHealth.softwareUpdateHealth.firmwareSoftwareUpdateStatus.currentVersion
                        CompanyPortalVersion = $TeamsDeviceHealth.softwareUpdateHealth.companyPortalSoftwareUpdateStatus.currentVersion
                        OEMAgentAppVersion = $TeamsDeviceHealth.softwareUpdateHealth.partnerAgentSoftwareUpdateStatus.currentVersion
                        TeamsAppVersion = $TeamsDeviceHealth.softwareUpdateHealth.teamsClientSoftwareUpdateStatus.currentVersion
                        
                        #LastOperation
                        LastHistoryAction = $LastHistoryAction
                        LastHistoryStatus = $LastHistoryStatus
                        LastHistoryInitiatedBy = $LastHistoryInitiatedBy
                        LastHistoryModifiedDate = $LastHistoryModifiedDate
                        LastHistoryErrorCode = $LastHistoryErrorCode
                        LastHistoryErrorMessage = $LastHistoryErrorMessage 
                    }
                    $TDObj.PSObject.TypeNames.Insert(0, 'TeamsDevice')
            
                } else {
                    $TDObj = New-Object -TypeName PSObject -Property @{
                        UserDisplayName = $TeamsDevice.currentuser.displayName
                        UserUPN         = $userUPN 
                
                        DeviceType      = Convert-UcTeamsDeviceType $TeamsDevice.deviceType

                        Manufacturer    = $TeamsDevice.hardwaredetail.manufacturer
                        Model           = $TeamsDevice.hardwaredetail.model
                        SerialNumber    = $TeamsDevice.hardwaredetail.serialNumber 
                        MacAddresses    = $TeamsDevice.hardwaredetail.macAddresses
                                    
                        DeviceHealth    = $TeamsDevice.healthStatus
                    }
                    $TDObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceList')
                }
                $outTeamsDevices.Add($TDObj) | Out-Null
            }
        }
    }
    $outTeamsDevices | Sort-Object DeviceType,Manufacturer,Model
}
#EndRegion '.\Public\Get-UcTeamsDevice.ps1' 329
#Region '.\Public\Get-UcTeamsForest.ps1' 0
<#
.SYNOPSIS
Get Teams Forest
 
.DESCRIPTION
This function returns the forest for a SIP enabled domain.
 
.PARAMETER Domain
Specifies a domain registered with Microsoft 365
 
.EXAMPLE
PS> Get-UcTeamsForest -Domain uclobby.com
#>

Function Get-UcTeamsForest {
    Param(
        [Parameter(Mandatory = $true)]
        [string]$Domain
    )
    $regex = "^.*[redirect].*(webdir)(\w*)(.online.lync.com).*$"
    try {
        $WebRequest = Invoke-WebRequest -Uri ("https://webdir.online.lync.com/AutoDiscover/AutoDiscoverservice.svc/root?originalDomain=" + $Domain)
        $temp = [regex]::Match($WebRequest, $regex).captures.groups
        $result = New-Object -TypeName PSObject -Property @{
                
            Domain       = $Domain
            Forest       = $temp[2].Value
            MigrationURL = "https://admin" + $temp[2].Value + ".online.lync.com/HostedMigration/hostedmigrationService.svc"
        }
        return $result
    }
    catch {
 
        if ($Error[0].Exception.Message -like "*404*") {
            Write-Warning ($Domain + " is not enabled for SIP." )
        }
    }
}
#EndRegion '.\Public\Get-UcTeamsForest.ps1' 38
#Region '.\Public\Get-UcTeamsVersion.ps1' 0

<#
.SYNOPSIS
Get Microsoft Teams Desktop Version
 
.DESCRIPTION
This function returns the installed Microsoft Teams desktop version for each user profile.
 
.PARAMETER $Path
Specify the path with Teams Log Files
 
.PARAMETER $Computer
Specify the remote computer
 
.EXAMPLE
PS> Get-UcTeamsVersion
 
.EXAMPLE
PS> Get-UcTeamsVersion -Path C:\Temp\
 
.EXAMPLE
PS> Get-UcTeamsVersion -Computer workstation124
 
.EXAMPLE
PS> $cred = Get-Credential
PS> Get-UcTeamsVersion -Computer workstation124 -Credential $cred
 
#>

Function Get-UcTeamsVersion {
    Param(
        [string]$Path,
        [string]$Computer,
        [System.Management.Automation.PSCredential]$Credential
    )
    $regexVersion = '("version":")([0-9.]*)'
    $regexRing = '("ring":")(\w*)'
    $regexEnv = '("environment":")(\w*)'
    $regexCloudEnv = '("cloudEnvironment":")(\w*)'
    $regexRegion = '("region":")([a-zA-Z0-9._-]*)'

    $regexWindowsUser = '("upnWindowUserUpn":")([a-zA-Z0-9@._-]*)'
    $regexTeamsUserName = '("userName":")([a-zA-Z0-9@._-]*)'

    $outTeamsVersion = [System.Collections.ArrayList]::new()

    if($Path){
        if (Test-Path $Path -ErrorAction SilentlyContinue) {
            $TeamsSettingsFiles = Get-ChildItem -Path $Path -Include "settings.json" -Recurse
            foreach ($TeamsSettingsFile in $TeamsSettingsFiles){
                $TeamsSettings = Get-Content -Path $TeamsSettingsFile.FullName
                $Version = ""
                $Ring = ""
                $Env = ""
                $CloudEnv = ""
                $Region = ""
                try {
                    $VersionTemp = [regex]::Match($TeamsSettings, $regexVersion).captures.groups
                    if ($VersionTemp.Count -ge 2) {
                        $Version = $VersionTemp[2].value
                    }
                    $RingTemp = [regex]::Match($TeamsSettings, $regexRing).captures.groups
                    if ($RingTemp.Count -ge 2) {
                        $Ring = $RingTemp[2].value
                    }
                    $EnvTemp = [regex]::Match($TeamsSettings, $regexEnv).captures.groups
                    if ($EnvTemp.Count -ge 2) {
                        $Env = $EnvTemp[2].value
                    }
                    $CloudEnvTemp = [regex]::Match($TeamsSettings, $regexCloudEnv).captures.groups
                    if ($CloudEnvTemp.Count -ge 2) {
                        $CloudEnv = $CloudEnvTemp[2].value
                    }
                    $RegionTemp = [regex]::Match($TeamsSettings, $regexRegion).captures.groups
                    if ($RegionTemp.Count -ge 2) {
                        $Region = $RegionTemp[2].value
                    }
                }
                catch { }
                $TeamsDesktopSettingsFile = $TeamsSettingsFile.Directory.FullName + "\desktop-config.json"
                if (Test-Path $TeamsDesktopSettingsFile -ErrorAction SilentlyContinue) {
                    $TeamsDesktopSettings = Get-Content -Path $TeamsDesktopSettingsFile
                    $WindowsUser = ""
                    $TeamsUserName =""
                    $RegexTemp = [regex]::Match($TeamsDesktopSettings, $regexWindowsUser).captures.groups
                    if ($RegexTemp.Count -ge 2) {
                        $WindowsUser = $RegexTemp[2].value
                    }
                    $RegexTemp = [regex]::Match($TeamsDesktopSettings, $regexTeamsUserName).captures.groups
                    if ($RegexTemp.Count -ge 2) {
                        $TeamsUserName = $RegexTemp[2].value
                    }
                }
                $TeamsVersion = New-Object -TypeName PSObject -Property @{
                    WindowsUser             = $WindowsUser
                    TeamsUser               = $TeamsUserName
                    Version                 = $Version
                    Ring                    = $Ring
                    Environment             = $Env
                    CloudEnvironment        = $CloudEnv
                    Region                  = $Region
                    Path                    = $TeamsSettingsFile.Directory.FullName
                }
                $TeamsVersion.PSObject.TypeNames.Insert(0, 'TeamsVersionFromPath')
                $outTeamsVersion.Add($TeamsVersion) | Out-Null
            }
        } else {
            Write-Error -Message ("Invalid Path, please check if path: " + $path + " is correct and exists.")
        }

    } else {
        $currentDateFormat = [cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern
        if($Computer) {
            $RemotePath = "\\" + $Computer + "\C$\Users"

            if($Credential){
                New-PSDrive -Root $RemotePath -Name ($Computer+"TmpTeamsVersion") -PSProvider FileSystem -Credential $Credential | Out-Null
            }

            if(Test-Path -Path $RemotePath) {
                $Profiles = Get-ChildItem -Path $RemotePath -ErrorAction SilentlyContinue
            } else {
                Write-Error -Message ("Cannot get users on : " + $computer + " check if name is correct and if the current user has permissions.")
            }
        } else {
            $Profiles = Get-childItem 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' | ForEach-Object { Get-ItemProperty $_.pspath } | Where-Object { $_.fullprofile -eq 1 }
        }
        
        foreach ($UserProfile in $Profiles) {
            if($Computer){
                $ProfilePath = $UserProfile.FullName
                $ProfileName = $UserProfile.Name
            } else {
                $ProfilePath = $UserProfile.ProfileImagePath
                $ProfileName  = (New-Object System.Security.Principal.SecurityIdentifier($UserProfile.PSChildName)).Translate( [System.Security.Principal.NTAccount]).Value
            }
            $TeamsSettingPath = $ProfilePath + "\AppData\Roaming\Microsoft\Teams\settings.json"
            if (Test-Path $TeamsSettingPath -ErrorAction SilentlyContinue) {
                $TeamsSettings = Get-Content -Path $TeamsSettingPath
                $Version = ""
                $Ring = ""
                $Env = ""
                $CloudEnv = ""
                $Region = ""
                try {
                    $VersionTemp = [regex]::Match($TeamsSettings, $regexVersion).captures.groups
                    if ($VersionTemp.Count -ge 2) {
                        $Version = $VersionTemp[2].value
                    }
                    $RingTemp = [regex]::Match($TeamsSettings, $regexRing).captures.groups
                    if ($RingTemp.Count -ge 2) {
                        $Ring = $RingTemp[2].value
                    }
                    $EnvTemp = [regex]::Match($TeamsSettings, $regexEnv).captures.groups
                    if ($EnvTemp.Count -ge 2) {
                        $Env = $EnvTemp[2].value
                    }
                    $CloudEnvTemp = [regex]::Match($TeamsSettings, $regexCloudEnv).captures.groups
                    if ($CloudEnvTemp.Count -ge 2) {
                        $CloudEnv = $CloudEnvTemp[2].value
                    }
                    $RegionTemp = [regex]::Match($TeamsSettings, $regexRegion).captures.groups
                    if ($RegionTemp.Count -ge 2) {
                        $Region = $RegionTemp[2].value
                    }
                }
                catch { }
                $TeamsApp = $ProfilePath + "\AppData\Local\Microsoft\Teams\current\Teams.exe"
                $InstallDateStr = Get-Content ($ProfilePath + "\AppData\Roaming\Microsoft\Teams\installTime.txt")
                $TeamsVersion = New-Object -TypeName PSObject -Property @{
                    Profile          = $ProfileName
                    ProfilePath      = $ProfilePath
                    Version          = $Version
                    Ring             = $Ring
                    Environment      = $Env
                    CloudEnvironment = $CloudEnv
                    Region           = $Region
                    Arch             = Get-UcArch $TeamsApp
                    InstallDate      = [Datetime]::ParseExact($InstallDateStr, 'M/d/yyyy', $null) | Get-Date -Format $currentDateFormat
                }
                $TeamsVersion.PSObject.TypeNames.Insert(0, 'TeamsVersion')
                $outTeamsVersion.Add($TeamsVersion) | Out-Null
            }
        }
        if($Credential -and $Computer){
            try{
                Remove-PSDrive -Name ($Computer+"TmpTeamsVersion") -ErrorAction SilentlyContinue
            } catch {}
        }
    }
    return $outTeamsVersion
}
#EndRegion '.\Public\Get-UcTeamsVersion.ps1' 192
#Region '.\Public\Get-UcTeamsWithSingleOwner.ps1' 0
<#
.SYNOPSIS
Get Teams that have a single owner
 
.DESCRIPTION
This function returns a list of Teams that only have a single owner.
 
.EXAMPLE
PS> Get-UcTeamsWithSingleOwner
#>

Function Get-UcTeamsWithSingleOwner {
    Get-UcTeamUsersEmail -Role Owner -Confirm:$false | Group-Object -Property TeamDisplayName | Where-Object { $_.Count -lt 2 } | Select-Object -ExpandProperty Group
}
#EndRegion '.\Public\Get-UcTeamsWithSingleOwner.ps1' 14
#Region '.\Public\Get-UcTeamUsersEmail.ps1' 0

<#
.SYNOPSIS
Get Users Email Address that are in a Team
 
.DESCRIPTION
This function returns a list of users email address that are part of a Team.
 
.PARAMETER TeamName
Specifies Team Name
 
.PARAMETER Role
Specifies which roles to filter (Owner, User, Guest)
 
.EXAMPLE
PS> Get-UcTeamUsersEmail
 
.EXAMPLE
PS> Get-UcTeamUsersEmail -TeamName "Marketing"
 
.EXAMPLE
PS> Get-UcTeamUsersEmail -Role "Guest"
 
.EXAMPLE
PS> Get-UcTeamUsersEmail -TeamName "Marketing" -Role "Guest"
#>

Function Get-UcTeamUsersEmail {
    [cmdletbinding(SupportsShouldProcess)]
    Param(
        [Parameter(Mandatory = $false)]
        [string]$TeamName,
        [Parameter(Mandatory = $false)]
        [ValidateSet("Owner", "User", "Guest")] 
        [string]$Role
    )
    $output = [System.Collections.ArrayList]::new()
    if ($TeamName) {
        $Teams = Get-Team -DisplayName $TeamName
    }
    else {
        if ($ConfirmPreference) {
            $title = 'Confirm'
            $question = 'Are you sure that you want to list all Teams?'
            $choices = '&Yes', '&No'
            $decision = $Host.UI.PromptForChoice($title, $question, $choices, 1)
        }
        else {
            $decision = 0
        }
        if ($decision -eq 0) {
            $Teams = Get-Team
        }
        else {
            return
        }
    }
    foreach ($Team in $Teams) { 
        if ($Role) {
            $TeamMembers = Get-TeamUser -GroupId $Team.GroupID -Role $Role
        }
        else {
            $TeamMembers = Get-TeamUser -GroupId $Team.GroupID 
        }
        foreach ($TeamMember in $TeamMembers) {
            $Email = ( Get-csOnlineUser $TeamMember.User | Select-Object @{Name = 'PrimarySMTPAddress'; Expression = { $_.ProxyAddresses -cmatch '^SMTP:' -creplace 'SMTP:' } }).PrimarySMTPAddress
            $Member = New-Object -TypeName PSObject -Property @{
                TeamGroupID     = $Team.GroupID
                TeamDisplayName = $Team.DisplayName
                TeamVisibility  = $Team.Visibility
                UPN             = $TeamMember.User
                Role            = $TeamMember.Role
                Email           = $Email
            }
            $Member.PSObject.TypeNames.Insert(0, 'TeamUsersEmail')
            $output.Add($Member) | Out-Null
        }
    }
    return $output
}
#EndRegion '.\Public\Get-UcTeamUsersEmail.ps1' 80
#Region '.\Public\Test-UcTeamsDevicesCompliancePolicy.ps1' 0
<#
.SYNOPSIS
Validate which Intune Compliance policies are supported by Microsoft Teams Android Devices
 
.DESCRIPTION
This function will validate each setting in the Intune Compliance Policy to make sure they are in line with the supported settings:
 
    https://docs.microsoft.com/en-us/microsoftteams/rooms/supported-ca-and-compliance-policies?tabs=phones#supported-device-compliance-policies
 
Contributors: Traci Herr, David Paulino
 
Requirements: Microsoft Graph PowerShell Module (Install-Module Microsoft.Graph)
 
.PARAMETER Detailed
Displays test results for unsupported settings in each Intune Compliance Policy
 
.PARAMETER All
Will check all Intune Compliance policies independently if they are assigned to a Group(s)
 
.PARAMETER IncludeSupported
Displays results for all settings in each Intune Compliance Policy
 
.PARAMETER PolicyID
Specifies a Policy ID that will be checked if is supported by Microsoft Teams Android Devices
 
.PARAMETER PolicyName
Specifies a Policy Name that will be checked if is supported by Microsoft Teams Android Devices
 
.PARAMETER UserUPN
Specifies a UserUPN that we want to check for applied compliance policies
 
.PARAMETER DeviceID
Specifies DeviceID that we want to check for applied compliance policies
 
.EXAMPLE
PS> Test-UcTeamsDevicesCompliancePolicy
 
.EXAMPLE
PS> Test-UcTeamsDevicesCompliancePolicy -Detailed
 
#>

Function Test-UcTeamsDevicesCompliancePolicy {
    Param(
        [switch]$Detailed,
        [switch]$All,
        [switch]$IncludeSupported,
        [string]$PolicyID,
        [string]$PolicyName,
        [string]$UserUPN,
        [string]$DeviceID
    )

    $connectedMSGraph = $false
    $CompliancePolicies = $null
    $totalCompliancePolicies = 0
    $skippedCompliancePolicies = 0

    $GraphURI_CompliancePolicies = "https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicies/"
    $GraphURI_Users = "https://graph.microsoft.com/v1.0/users"
    $GraphURI_Devices = "https://graph.microsoft.com/v1.0/devices"

    $SupportedAndroidCompliancePolicies = "#microsoft.graph.androidCompliancePolicy","#microsoft.graph.androidDeviceOwnerCompliancePolicy","#microsoft.graph.aospDeviceOwnerCompliancePolicy"
    $SupportedWindowsCompliancePolicies = "#microsoft.graph.windows10CompliancePolicy"

    $URLSupportedCompliancePoliciesAndroid = "https://aka.ms/TeamsDevicePolicies?tabs=phones#supported-device-compliance-policies"
    $URLSupportedCompliancePoliciesWindows = "https://aka.ms/TeamsDevicePolicies?tabs=mtr-w#supported-device-compliance-policies"

    $scopes = (Get-MgContext).Scopes

    if (!($scopes) -or !(("DeviceManagementConfiguration.Read.All" -in $scopes) -and ("Directory.Read.All" -in $scopes))) {
        Connect-MgGraph -Scopes "DeviceManagementConfiguration.Read.All","Directory.Read.All"
    }

    try {
        $CompliancePolicies = (Invoke-MgGraphRequest -Uri $GraphURI_CompliancePolicies -Method GET).value
        $connectedMSGraph = $true
    }
    catch [System.Net.Http.HttpRequestException]{
        if ($PSItem.Exception.Response.StatusCode -eq "Unauthorized") {
            Write-Error "Access Denied, please make sure the user connecing to MS Graph is part of one of the following Global Reader/Intune Service Administrator/Global Administrator roles"
        }
        else {
            Write-Error $PSItem.Exception.Message
        }
    }
    catch {
        Write-Error 'Please connect to MS Graph with Connect-MgGraph -Scopes "DeviceManagementConfiguration.Read.All","Directory.Read.All" before running this script'
    }

    if ($connectedMSGraph) {
        $output = [System.Collections.ArrayList]::new()
        $outputSum = [System.Collections.ArrayList]::new()
        if($UserUPN)
        {
            try{
                $UserGroups = (Invoke-MgGraphRequest -Uri ($GraphURI_Users + "/" + $userUPN + "/transitiveMemberOf?`$select=id") -Method GET).value.id
            } catch [System.Net.Http.HttpRequestException] {
                if($PSItem.Exception.Response.StatusCode -eq "NotFound"){
                    Write-warning -Message ("User Not Found: "+ $UserUPN)
                }
                return
            }
            #We also need to take in consideration devices that are registered to this user
            $DeviceGroups = [System.Collections.ArrayList]::new()
            $userDevices = (Invoke-MgGraphRequest -Uri ($GraphURI_Users + "/" + $userUPN + "/registeredDevices?`$select=deviceId,displayName") -Method GET).value
            foreach($userDevice in $userDevices){
                $tmpGroups = (Invoke-MgGraphRequest -Uri ($GraphURI_Devices + "(deviceId='{" + $userDevice.deviceID + "}')/transitiveMemberOf?`$select=id") -Method GET).value.id
                foreach($tmpGroup in $tmpGroups){
                    $tmpDG = New-Object -TypeName PSObject -Property @{
                        GroupId             = $tmpGroup
                        DeviceId            = $userDevice.deviceID
                        DeviceDisplayName   = $userDevice.displayName
                        }
                    $DeviceGroups.Add($tmpDG) | Out-Null
                }
            }
        }
        
        if($DeviceID){
            try{
                $DeviceGroups = (Invoke-MgGraphRequest -Uri ($GraphURI_Devices + "(deviceId='{" + $DeviceID + "}')/transitiveMemberOf?`$select=id") -Method GET).value.id
            } catch [System.Net.Http.HttpRequestException] {
                if($PSItem.Exception.Response.StatusCode -eq "BadRequest"){
                    Write-warning -Message ("Device ID Not Found: "+ $DeviceID)
                }
                return
            }
        }

        $Groups =  Get-MgGroup -Select Id,DisplayName -All
        foreach ($CompliancePolicy in $CompliancePolicies) {
            if ((($PolicyID -eq $CompliancePolicy.id) -or ($PolicyName -eq $CompliancePolicy.displayName) -or (!$PolicyID -and !$PolicyName)) -and (($CompliancePolicy."@odata.type" -in $SupportedAndroidCompliancePolicies) -or ($CompliancePolicy."@odata.type" -in $SupportedWindowsCompliancePolicies))) {

                #We need to check if the policy has assignments (Groups/All Users/All Devices)
                $CompliancePolicyAssignments =  (Invoke-MgGraphRequest -Uri ($GraphURI_CompliancePolicies + $CompliancePolicy.id + "/assignments" ) -Method GET).value
                $AssignedToGroup = [System.Collections.ArrayList]::new()
                $ExcludedFromGroup =[System.Collections.ArrayList]::new()
                $outAssignedToGroup = ""
                $outExcludedFromGroup =""
                #We wont need to check the settings if the policy is not assigned to a user
                if($UserUPN -or $DeviceID){
                    $userOrDeviceIncluded = $false
                } else {
                    $userOrDeviceIncluded = $true
                }

                #Define the Compliance Policy type
                switch ($CompliancePolicy."@odata.type") {
                    "#microsoft.graph.androidCompliancePolicy"              { $CPType = "Android Device" }
                    "#microsoft.graph.androidDeviceOwnerCompliancePolicy"   { $CPType = "Android Enterprise" }
                    "#microsoft.graph.aospDeviceOwnerCompliancePolicy"      { $CPType = "Android (AOSP)" }
                    "#microsoft.graph.windows10CompliancePolicy"            { $CPType = "Windows 10 or later" }
                    Default { $CPType = $CompliancePolicy."@odata.type".split('.')[2] }
                }

                #Checking Compliance Policy assigments since we can skip non assigned policies.
                foreach($CompliancePolicyAssignment in $CompliancePolicyAssignments){
                    $GroupEntry = New-Object -TypeName PSObject -Property @{
                        GroupID = $CompliancePolicyAssignment.target.Groupid
                        GroupDisplayName   = ($Groups | Where-Object -Property id -EQ -Value $CompliancePolicyAssignment.target.Groupid).displayName
                    }
                    switch ($CompliancePolicyAssignment.target."@odata.type"){
                        #Policy assigned to all users
                        "#microsoft.graph.allLicensedUsersAssignmentTarget" {
                            $GroupEntry = New-Object -TypeName PSObject -Property @{
                                GroupID = "allLicensedUsersAssignment"
                                GroupDisplayName   = "All Users"
                            }
                            $AssignedToGroup.Add($GroupEntry) | Out-Null
                            $userOrDeviceIncluded = $true
                        }
                        #Policy assigned to all devices
                        "#microsoft.graph.allDevicesAssignmentTarget" {
                            $GroupEntry = New-Object -TypeName PSObject -Property @{
                                GroupID = "allDevicesAssignmentTarget"
                                GroupDisplayName   = "All Devices"
                            }
                            $AssignedToGroup.Add($GroupEntry) | Out-Null
                            $userOrDeviceIncluded = $true
                        }
                        #Group that this policy is assigned
                        "#microsoft.graph.groupAssignmentTarget" {
                            $AssignedToGroup.Add($GroupEntry) | Out-Null
                            if(($UserUPN -or $DeviceID) -and (($CompliancePolicyAssignment.target.Groupid -in $UserGroups) -or ($CompliancePolicyAssignment.target.Groupid -in $DeviceGroups))){
                                $userOrDeviceIncluded = $true
                            }
                        }
                        #Group that this policy is excluded
                        "#microsoft.graph.exclusionGroupAssignmentTarget" {
                            $ExcludedFromGroup.Add($GroupEntry)| Out-Null
                            #If user is excluded then we dont need to check the policy
                            if($UserUPN -and ($CompliancePolicyAssignment.target.Groupid -in $UserGroups)){
                                Write-Warning ("Skiping compliance policy " +$CompliancePolicy.displayName + ", since user " + $UserUPN +" is part of an Excluded Group: " + $GroupEntry.GroupDisplayName)
                                $userOrDeviceExcluded = $true
                            } elseif($DeviceID -and ($CompliancePolicyAssignment.target.Groupid -in $DeviceGroups)){
                                Write-Warning ("Skiping compliance policy " +$CompliancePolicy.displayName + ", since device " + $DeviceID +" is part of an Excluded Group: " + $GroupEntry.GroupDisplayName)
                                $userOrDeviceExcluded = $true
                            } elseif($UserUPN -and (($CompliancePolicyAssignment.target.Groupid -in $DeviceGroups.GroupId))){
                                #In case a device is excluded we will check the policy but output a message
                                $tmpDev = ($DeviceGroups | Where-Object -Property GroupId -eq -Value $CompliancePolicyAssignment.target.Groupid)
                                Write-Warning ("Compliance policy " +$CompliancePolicy.displayName + " will not be applied to device " + $tmpDev.DeviceDisplayName +" (" + $tmpDev.DeviceID +"), since this device is part of an Excluded Group: " + $GroupEntry.GroupDisplayName)
                            }
                        }
                    }
                }
                
                if((($AssignedToGroup.count -gt 0) -and !$userOrDeviceExcluded -and $userOrDeviceIncluded) -or $all){
                    $totalCompliancePolicies++ 
                    $PolicyErrors = 0
                    $PolicyWarnings = 0

                    #If only assigned/excluded from a group we will show the group display name, otherwise the number of groups assigned/excluded.
                    if($AssignedToGroup.count -eq 1){
                        $outAssignedToGroup = $AssignedToGroup.GroupDisplayName
                    } elseif($AssignedToGroup.count -eq 0){
                        $outAssignedToGroup = "None"
                    } else {
                        $outAssignedToGroup = "" + $AssignedToGroup.count + " groups"
                    }

                    if($ExcludedFromGroup.count -eq 1){
                        $outExcludedFromGroup = $ExcludedFromGroup.GroupDisplayName
                    } elseif($ExcludedFromGroup.count -eq 0){
                        $outExcludedFromGroup = "None"
                    } else {
                        $outExcludedFromGroup = "" + $ExcludedFromGroup.count + " groups"
                    }

                    if($CompliancePolicy."@odata.type" -in $SupportedAndroidCompliancePolicies){
                        $URLSupportedCompliancePolicies = $URLSupportedCompliancePoliciesAndroid
                    }
                    elseif($CompliancePolicy."@odata.type" -in $SupportedWindowsCompliancePolicies){
                        $URLSupportedCompliancePolicies = $URLSupportedCompliancePoliciesWindows
                    }

                #region Common settings between Android and Windows
                    #region 9: Device Properties > Operation System Version
                    $ID = 9.1
                    $Setting = "osMinimumVersion"
                    $SettingDescription = "Device Properties > Operation System Version > Minimum OS version"
                    $SettingValue = "Not Configured"
                    $Comment = ""
                    if (!([string]::IsNullOrEmpty($CompliancePolicy.osMinimumVersion))) {
                        if ($CompliancePolicy."@odata.type" -in $SupportedWindowsCompliancePolicies){
                            $Status = "Unsupported"
                            $Comment = "Teams Rooms automatically updates to newer versions of Windows and setting values here could prevent successful sign-in after an OS update."
                            $PolicyErrors++
                        } else {
                            $Status = "Warning"
                            $Comment = "This setting can cause sign in issues."
                            $PolicyWarnings++
                        }
                        $SettingValue = $CompliancePolicy.osMinimumVersion
                    }
                    else {
                        $Status = "Supported"
                    }
                    $SettingPSObj =  [PSCustomObject]@{
                        PolicyName              = $CompliancePolicy.displayName
                        PolicyType              = $CPType
                        Setting                 = $Setting
                        Value                   = $SettingValue
                        TeamsDevicesStatus      = $Status 
                        Comment                 = $Comment
                        SettingDescription      = $SettingDescription
                        AssignedToGroup         = $outAssignedToGroup
                        ExcludedFromGroup       = $outExcludedFromGroup 
                        AssignedToGroupList     = $AssignedToGroup
                        ExcludedFromGroupList   = $ExcludedFromGroup
                        PolicyID                = $CompliancePolicy.id
                        ID                      = $ID
                    }
                    $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                    [void]$output.Add($SettingPSObj)
            
                    $ID = 9.2
                    $Setting = "osMaximumVersion"
                    $SettingDescription = "Device Properties > Operation System Version > Maximum OS version"
                    $SettingValue = "Not Configured"
                    $Comment = ""
                    if (!([string]::IsNullOrEmpty($CompliancePolicy.osMaximumVersion))) {
                        if ($CompliancePolicy."@odata.type" -in $SupportedWindowsCompliancePolicies){
                            $Status = "Unsupported"
                            $Comment = "Teams Rooms automatically updates to newer versions of Windows and setting values here could prevent successful sign-in after an OS update."
                            $PolicyErrors++
                        } else {
                            $Status = "Warning"
                            $Comment = "This setting can cause sign in issues."
                            $PolicyWarnings++
                        }
                        $SettingValue = $CompliancePolicy.osMaximumVersion
                    }
                    else {
                        $Status = "Supported"
                    }
                    $SettingPSObj =  [PSCustomObject]@{
                        PolicyName              = $CompliancePolicy.displayName
                        PolicyType              = $CPType
                        Setting                 = $Setting
                        Value                   = $SettingValue
                        TeamsDevicesStatus      = $Status 
                        Comment                 = $Comment
                        SettingDescription      = $SettingDescription
                        AssignedToGroup         = $outAssignedToGroup
                        ExcludedFromGroup       = $outExcludedFromGroup 
                        AssignedToGroupList     = $AssignedToGroup
                        ExcludedFromGroupList   = $ExcludedFromGroup
                        PolicyID                = $CompliancePolicy.id
                        ID                      = $ID
                    }
                    $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                    [void]$output.Add($SettingPSObj)
                    #endregion

                    #region 17: System Security > All Android devices > Require a password to unlock mobile devices
                    $ID = 17
                    $Setting = "passwordRequired"
                    $SettingDescription = "System Security > All Android devices > Require a password to unlock mobile devices"
                    $SettingValue = "Not Configured"
                    $Comment = ""
                    if ($CompliancePolicy.passwordRequired) {
                        $Status = "Unsupported"
                        $SettingValue = "Require"
                        $Comment = $URLSupportedCompliancePolicies
                        $PolicyErrors++
                    }
                    else {
                        $Status = "Supported"
                    }
                    $SettingPSObj =  [PSCustomObject]@{
                        PolicyName              = $CompliancePolicy.displayName
                        PolicyType              = $CPType
                        Setting                 = $Setting
                        Value                   = $SettingValue
                        TeamsDevicesStatus      = $Status 
                        Comment                 = $Comment
                        SettingDescription      = $SettingDescription
                        AssignedToGroup         = $outAssignedToGroup
                        ExcludedFromGroup       = $outExcludedFromGroup 
                        AssignedToGroupList     = $AssignedToGroup
                        ExcludedFromGroupList   = $ExcludedFromGroup
                        PolicyID                = $CompliancePolicy.id
                        ID                      = $ID
                    }
                    $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                    [void]$output.Add($SettingPSObj)
                    #endregion
                #endregion

                    if($CompliancePolicy."@odata.type" -in $SupportedAndroidCompliancePolicies){

                        #region 1: Microsoft Defender for Endpoint > Require the device to be at or under the machine risk score
                        $ID = 1
                        $Setting = "deviceThreatProtectionEnabled"
                        $SettingDescription = "Microsoft Defender for Endpoint > Require the device to be at or under the machine risk score"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if ($CompliancePolicy.deviceThreatProtectionEnabled) {
                            $Status = "Unsupported"
                            $PolicyErrors++
                            $SettingValue = $CompliancePolicy.advancedThreatProtectionRequiredSecurityLevel
                            $Comment = $URLSupportedCompliancePolicies
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                        #endregion

                        #region 2: Device Health > Device managed with device administrator
                        $ID = 2
                        $Setting = "securityBlockDeviceAdministratorManagedDevices"
                        $SettingDescription = "Device Health > Device managed with device administrator"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if ($CompliancePolicy.securityBlockDeviceAdministratorManagedDevices) {
                            $Status = "Unsupported"
                            $SettingValue = "Block"
                            $Comment =     "Teams Android devices management requires device administrator to be enabled."
                            $PolicyErrors++
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                        #endregion

                        #region 3: Device Health > Rooted devices
                        $ID = 3
                        $Setting = "securityBlockJailbrokenDevices"
                        $SettingDescription = "Device Health > Rooted devices"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if ($CompliancePolicy.securityBlockJailbrokenDevices) {
                            $Status = "Warning"
                            $SettingValue = "Block"
                            $Comment = "This setting can cause sign in issues."
                            $PolicyWarnings++
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                        #endregion

                        #region 4: Device Health > Require the device to be at or under the Device Threat Level
                        $ID = 4
                        $Setting = "deviceThreatProtectionRequiredSecurityLevel"
                        $SettingDescription = "Device Health > Require the device to be at or under the Device Threat Level"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if ($CompliancePolicy.deviceThreatProtectionRequiredSecurityLevel -ne "unavailable") {
                            $Status = "Unsupported"
                            $SettingValue = $CompliancePolicy.deviceThreatProtectionRequiredSecurityLevel
                            $Comment = $URLSupportedCompliancePolicies
                            $PolicyErrors++
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                        #endregion

                        #region 5: Device Health > Google Protect > Google Play Services is Configured
                        $ID = 5
                        $Setting = "securityRequireGooglePlayServices"
                        $SettingDescription = "Device Health > Google Protect > Google Play Services is Configured"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if ($CompliancePolicy.securityRequireGooglePlayServices) {
                            $Status = "Unsupported"
                            $SettingValue = "Require"
                            $Comment = "Google play isn't installed on Teams Android devices."
                            $PolicyErrors++
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                        #endregion

                        #region 6: Device Health > Google Protect > Up-to-date security provider
                        $ID = 6
                        $Setting = "securityRequireUpToDateSecurityProviders"
                        $SettingDescription = "Device Health > Google Protect > Up-to-date security provider"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if ($CompliancePolicy.securityRequireUpToDateSecurityProviders) {
                            $Status = "Unsupported"
                            $SettingValue = "Require"
                            $Comment = "Google play isn't installed on Teams Android devices."
                            $PolicyErrors++
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                        #endregion
                
                        #region 7: Device Health > Google Protect > Threat scan on apps
                        $ID = 7
                        $Setting = "securityRequireVerifyApps"
                        $SettingDescription = "Device Health > Google Protect > Threat scan on apps"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if ($CompliancePolicy.securityRequireVerifyApps) {
                            $Status = "Unsupported"
                            $SettingValue = "Require"
                            $Comment = "Google play isn't installed on Teams Android devices."
                            $PolicyErrors++
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                        #endregion

                        #region 8: Device Health > Google Protect > SafetyNet device attestation
                        $ID = 8
                        $Setting = "securityRequireSafetyNetAttestation"
                        $SettingDescription = "Device Health > Google Protect > SafetyNet device attestation"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if (($CompliancePolicy.securityRequireSafetyNetAttestationBasicIntegrity) -or ($CompliancePolicy.securityRequireSafetyNetAttestationCertifiedDevice)) {
                            $Status = "Unsupported"
                            $Comment = "Google play isn't installed on Teams Android devices."
                            $PolicyErrors++
                            if ($CompliancePolicy.securityRequireSafetyNetAttestationCertifiedDevice){
                                $SettingValue = "Check basic integrity and certified devices"
                            } elseif ($CompliancePolicy.securityRequireSafetyNetAttestationBasicIntegrity){
                                $SettingValue = "Check basic integrity"
                            }
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                        #endregion

                        

                        #region 10: System Security > Encryption > Require encryption of data storage on device.
                        $ID = 10
                        $Setting = "storageRequireEncryption"
                        $SettingDescription = "System Security > Encryption > Require encryption of data storage on device"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if ($CompliancePolicy.storageRequireEncryption) {
                            $Status = "Warning"
                            $SettingValue = "Require"
                            $Comment = "Manufacturers might configure encryption attributes on their devices in a way that Intune doesn't recognize. If this happens, Intune marks the device as noncompliant."
                            $PolicyWarnings++
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                        #endregion

                        #region 11: System Security > Device Security > Block apps from unknown sources
                        $ID = 11
                        $Setting = "securityPreventInstallAppsFromUnknownSources"
                        $SettingDescription = "System Security > Device Security > Block apps from unknown sources"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if ($CompliancePolicy.securityPreventInstallAppsFromUnknownSources) {
                            $Status = "Unsupported"
                            $SettingValue = "Block"
                            $Comment = "Only Teams admins install apps or OEM tools"
                            $PolicyErrors++
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                        #endregion

                        #region 14: System Security > Device Security > Minimum security patch level
                        $ID = 14
                        $Setting = "minAndroidSecurityPatchLevel"
                        $SettingDescription = "System Security > Device Security > Minimum security patch level"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if (!([string]::IsNullOrEmpty($CompliancePolicy.minAndroidSecurityPatchLevel))){
                            $Status = "Warning"
                            $SettingValue = $CompliancePolicy.minAndroidSecurityPatchLevel
                            $Comment = "This setting can cause sign in issues."
                            $PolicyWarnings++
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                        #endregion

                        #region 15: System Security > Device Security > Restricted apps
                        $ID = 15
                        $Setting = "securityPreventInstallAppsFromUnknownSources"
                        $SettingDescription = "System Security > Device Security > Restricted apps"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if (($CompliancePolicy.restrictedApps).count -gt 0 ) {
                            $Status = "Unsupported"
                            $SettingValue = "Found " + ($CompliancePolicy.restrictedApps).count  + " restricted app(s)"
                            $Comment = $URLSupportedCompliancePolicies
                            $PolicyErrors++
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                        #endregion

                        #region 16: System Security > All Android devices > Maximum minutes of inactivity before password is required
                        $ID = 16
                        $Setting = "passwordMinutesOfInactivityBeforeLock"
                        $SettingDescription = "System Security > All Android devices > Maximum minutes of inactivity before password is required"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if (!([string]::IsNullOrEmpty($CompliancePolicy.passwordMinutesOfInactivityBeforeLock))) {
                            $Status = "Unsupported"
                            $SettingValue = "" + $CompliancePolicy.passwordMinutesOfInactivityBeforeLock + " minutes"
                            $Comment = $URLSupportedCompliancePolicies
                            $PolicyErrors++
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                        #endregion
                    } 
                    elseif($CompliancePolicy."@odata.type" -in $SupportedWindowsCompliancePolicies){

                        #region 18: Device Properties > Operation System Version
                        $ID = 18.1
                        $Setting = "mobileOsMinimumVersion"
                        $SettingDescription = "Device Properties > Operation System Version > Minimum OS version for mobile devices"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if (!([string]::IsNullOrEmpty($CompliancePolicy.mobileOsMinimumVersion))) {
                            $Status = "Unsupported"
                            $SettingValue = $CompliancePolicy.mobileOsMinimumVersion
                            $Comment = $URLSupportedCompliancePolicies
                            $PolicyErrors++
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                
                        $ID = 18.2
                        $Setting = "mobileOsMaximumVersion"
                        $SettingDescription = "Device Properties > Operation System Version > Maximum OS version for mobile devices"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if (!([string]::IsNullOrEmpty($CompliancePolicy.mobileOsMaximumVersion))) {
                            $Status = "Unsupported"
                            $SettingValue = $CompliancePolicy.mobileOsMaximumVersion
                            $Comment = $URLSupportedCompliancePolicies
                            $PolicyErrors++
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                        #endregion

                        #region 19: Device Properties > Operation System Version > Valid operating system builds
                        $ID = 19
                        $Setting = "validOperatingSystemBuildRanges"
                        $SettingDescription = "Device Properties > Operation System Version > Valid operating system builds"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if (!([string]::IsNullOrEmpty($CompliancePolicy.validOperatingSystemBuildRanges))) {
                            $Status = "Unsupported"
                            $SettingValue = "Found " + ($CompliancePolicy.validOperatingSystemBuildRanges).count + " valid OS configured build(s)"
                            $Comment = $URLSupportedCompliancePolicies
                            $PolicyErrors++
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                        #endregion

                        #region 20: System Security > Defender > Microsoft Defender Antimalware minimum version
                        $ID = 20
                        $Setting = "defenderVersion"
                        $SettingDescription = "System Security > Defender > Microsoft Defender Antimalware minimum version"
                        $SettingValue = "Not Configured"
                        $Comment = ""
                        if (!([string]::IsNullOrEmpty($CompliancePolicy.defenderVersion))) {
                            $Status = "Unsupported"
                            $SettingValue = $CompliancePolicy.defenderVersion
                            $Comment = "Teams Rooms automatically updates this component so there's no need to set compliance policies."
                            $PolicyErrors++
                        }
                        else {
                            $Status = "Supported"
                        }
                        $SettingPSObj =  [PSCustomObject]@{
                            PolicyName              = $CompliancePolicy.displayName
                            PolicyType              = $CPType
                            Setting                 = $Setting
                            Value                   = $SettingValue
                            TeamsDevicesStatus      = $Status 
                            Comment                 = $Comment
                            SettingDescription      = $SettingDescription
                            AssignedToGroup         = $outAssignedToGroup
                            ExcludedFromGroup       = $outExcludedFromGroup 
                            AssignedToGroupList     = $AssignedToGroup
                            ExcludedFromGroupList   = $ExcludedFromGroup
                            PolicyID                = $CompliancePolicy.id
                            ID                      = $ID
                        }
                        $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicyDetailed')
                        [void]$output.Add($SettingPSObj)
                        #endregion
                    }

                    if ($PolicyErrors -gt 0) {
                        $StatusSum = "Found " + $PolicyErrors + " unsupported settings."
                        $displayWarning = $true
                    }
                    elseif ($PolicyWarnings -gt 0) {
                        $StatusSum = "Found " + $PolicyWarnings + " settings that may impact users."
                        $displayWarning = $true
                    }
                    else {
                        $StatusSum = "No issues found."
                    }
                    $PolicySum =  [PSCustomObject]@{
                        PolicyID                = $CompliancePolicy.id
                        PolicyName              = $CompliancePolicy.displayName
                        PolicyType              = $CPType
                        AssignedToGroup         = $outAssignedToGroup
                        AssignedToGroupList     = $AssignedToGroup
                        ExcludedFromGroup       = $outExcludedFromGroup 
                        ExcludedFromGroupList   = $ExcludedFromGroup
                        TeamsDevicesStatus      = $StatusSum
                    }
                    $PolicySum.PSObject.TypeNames.Insert(0, 'TeamsDeviceCompliancePolicy')
                    $outputSum.Add($PolicySum) | Out-Null
                } elseif (($AssignedToGroup.count -eq 0) -and !($UserUPN -or $DeviceID -or $Detailed)) {
                    $skippedCompliancePolicies++
                }
            }
        }
        if($totalCompliancePolicies -eq 0){
            if($UserUPN){
                Write-Warning ("The user " + $UserUPN + " doesn't have any Compliance Policies assigned.")
            } else {
                Write-Warning "No Compliance Policies assigned to All Users, All Devices or group found. Please use Test-UcTeamsDevicesCompliancePolicy -All to check all policies."
            }
        }
        if($IncludeSupported -and $Detailed)
        {
            $output | Sort-Object PolicyName, ID
        } elseif ($Detailed) {
            if ((( $output | Where-Object -Property TeamsDevicesStatus -NE -Value "Supported").count -eq 0) -and !$IncludeSupported){
                Write-Warning "No unsupported settings found, please use Test-UcTeamsDevicesCompliancePolicy -IncludeSupported to output all settings."
            } else {
                $output | Where-Object -Property TeamsDevicesStatus -NE -Value "Supported" | Sort-Object PolicyName, ID
            }
        }
        else {
            if(($skippedCompliancePolicies -gt 0) -and !$All){
                Write-Warning ("Skipping $skippedCompliancePolicies compliance policies since will not be applied to Teams Devices.")
                Write-Warning ("Please use the All switch to check all policies: Test-UcTeamsDevicesCompliancePolicy -All")
            }
            if($displayWarning){
                Write-Warning "One or more policies contain unsupported settings, please use Test-UcTeamsDevicesCompliancePolicy -Detailed to identify the unsupported settings."
            }
            $outputSum | Sort-Object PolicyName
        }
    }
}
#EndRegion '.\Public\Test-UcTeamsDevicesCompliancePolicy.ps1' 994
#Region '.\Public\Test-UcTeamsDevicesConditionalAccessPolicy.ps1' 0
<#
.SYNOPSIS
Validate which Conditional Access policies are supported by Microsoft Teams Android Devices
 
.DESCRIPTION
This function will validate each setting in a Conditional Access Policy to make sure they are in line with the supported settings:
 
    https://docs.microsoft.com/microsoftteams/rooms/supported-ca-and-compliance-policies?tabs=phones#conditional-access-policies"
 
Contributors: Traci Herr, David Paulino
 
Requirements: Microsoft Graph PowerShell Module (Install-Module Microsoft.Graph)
 
.PARAMETER Detailed
Displays test results for all settings in each Conditional Access Policy
 
.PARAMETER All
Will check all Conditional Access policies independently if they are assigned to a Group(s) or to Teams
 
.PARAMETER IncludeSupported
Displays results for all settings in each Conditional Access Policy
 
.PARAMETER UserUPN
Specifies a UserUPN that we want to check for applied Conditional Access policies
 
.EXAMPLE
PS> Test-UcTeamsDevicesConditionalAccessPolicy
 
.EXAMPLE
PS> Test-UcTeamsDevicesConditionalAccessPolicy -All
 
.EXAMPLE
PS> Test-UcTeamsDevicesConditionalAccessPolicy -Detailed
 
.EXAMPLE
PS> Test-UcTeamsDevicesConditionalAccessPolicy -Detailed -IncludedSupported
 
.EXAMPLE
PS> Test-UcTeamsDevicesConditionalAccessPolicy -UserUPN
 
#>


Function Test-UcTeamsDevicesConditionalAccessPolicy {
    Param(
        [switch]$Detailed,
        [switch]$All,
        [switch]$IncludeSupported,
        [string]$UserUPN
    )

    $GraphURI_Users = "https://graph.microsoft.com/v1.0/users"
    $GraphURI_ConditionalAccess = "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies"

    $connectedMSGraph = $false
    $ConditionalAccessPolicies = $null
    $totalCAPolicies = 0
    $skippedCAPolicies = 0

    $URLTeamsDevicesCA = "https://aka.ms/TeamsDevicePolicies#supported-conditional-access-policies"
    $URLTeamsDevicesKnownIssues = "https://docs.microsoft.com/microsoftteams/troubleshoot/teams-rooms-and-devices/rooms-known-issues#teams-phone-devices"

    $scopes = (Get-MgContext).Scopes
    if (!($scopes) -or !(("Policy.Read.All" -in $scopes )-and ("Directory.Read.All" -in $scopes))) {
        Connect-MgGraph -Scopes "Policy.Read.All","Directory.Read.All"
    }

    try {
        $ConditionalAccessPolicies = (Invoke-MgGraphRequest -Uri ($GraphURI_ConditionalAccess + $GraphFilter) -Method GET).Value
        $connectedMSGraph = $true
    }
    catch [System.Net.Http.HttpRequestException]{
        if ($PSItem.Exception.Response.StatusCode -eq "Forbidden") {
            Write-Error "Access Denied, please make sure the user connecing to MS Graph is part of one of the following Global Reader/Conditional Access Administrator/Global Administrator roles"
        }
        else {
            Write-Error $PSItem.Exception.Message
        }
    }
    catch {
        Write-Error 'Please connect to MS Graph with Connect-MgGraph -Scopes "Policy.Read.All","Directory.Read.All" before running this script'
    }

    if ($connectedMSGraph) {
        $output = [System.Collections.ArrayList]::new()
        $outputSum = [System.Collections.ArrayList]::new()
        if($UserUPN)
        {
            try{
                $UserID = (Invoke-MgGraphRequest -Uri ($GraphURI_Users + "/" + $userUPN + "?`$select=id") -Method GET).id
                $UserGroups = (Invoke-MgGraphRequest -Uri ($GraphURI_Users + "/" + $userUPN + "/transitiveMemberOf?`$select=id") -Method GET).value.id
            } catch [System.Net.Http.HttpRequestException] {
                if($PSItem.Exception.Response.StatusCode -eq "NotFound"){
                    Write-warning -Message ("User Not Found: "+ $UserUPN)
                }
                return
            }
        }
        #Using the PowerShell Module so simplify the request in case we have more than 100 groups.
        $Groups =  Get-MgGroup -Select Id,DisplayName -All
        $ServicePrincipals = Get-MgServicePrincipal -Select AppId,DisplayName -All
        foreach ($ConditionalAccessPolicy in $ConditionalAccessPolicies) {
            $AssignedToGroup = [System.Collections.ArrayList]::new()
            $ExcludedFromGroup =[System.Collections.ArrayList]::new()
            $AssignedToUserCount = 0
            $ExcludedFromUserCount = 0
            $outAssignedToGroup = ""
            $outExcludedFromGroup =""
            $userExcluded = $false
            $StatusSum = ""
            
            $totalCAPolicies++
            $PolicyErrors = 0
            $PolicyWarnings = 0

            if($UserUPN){
                if($UserID -in $ConditionalAccessPolicy.conditions.users.excludeUsers){
                    $userExcluded = $true
                    Write-Warning ("Skiping conditional access policy " + $ConditionalAccessPolicy.displayName + ", since user " + $UserUPN +" is part of Excluded Users")
                } elseif($UserID -in $ConditionalAccessPolicy.conditions.users.includeUsers){
                    $userIncluded = $true
                } else {
                    $userIncluded = $false
                }
            } else {
                $userIncluded = $true
            }

            #All Users in Conditional Access Policy will show as a 'All' in the includeUsers.
            if("All" -in $ConditionalAccessPolicy.conditions.users.includeUsers){
                $GroupEntry = New-Object -TypeName PSObject -Property @{
                    GroupID = "All"
                    GroupDisplayName   = "All Users"
                }
                $AssignedToGroup.Add($GroupEntry) | Out-Null
                $userIncluded = $true
            } elseif ((($ConditionalAccessPolicy.conditions.users.includeUsers).count -gt 0) -and "None" -notin $ConditionalAccessPolicy.conditions.users.includeUsers){
                $AssignedToUserCount = ($ConditionalAccessPolicy.conditions.users.includeUsers).count
                $userIncluded = $true
            }
            foreach($includedGroup in $ConditionalAccessPolicy.conditions.users.includeGroups){
                $GroupEntry = New-Object -TypeName PSObject -Property @{
                    GroupID = $includedGroup
                    GroupDisplayName   = ($Groups | Where-Object -Property Id -EQ -Value $includedGroup).displayName
                }
                $AssignedToGroup.Add($GroupEntry) | Out-Null
                if($includedGroup -in $UserGroups){
                    $userIncluded = $true
                }
            }

            
            foreach($excludedGroup in $ConditionalAccessPolicy.conditions.users.excludeGroups){
                $GroupEntry = New-Object -TypeName PSObject -Property @{
                    GroupID = $excludedGroup
                    GroupDisplayName   = ($Groups | Where-Object -Property id -EQ -Value $excludedGroup).displayName
                }
                $ExcludedFromGroup.Add($GroupEntry) | Out-Null                
                if($excludedGroup -in $UserGroups){
                    $userExcluded = $true
                    Write-Warning ("Skiping conditional access policy " + $ConditionalAccessPolicy.displayName + ", since user " + $UserUPN +" is part of an Excluded Group: " + $GroupEntry.GroupDisplayName)
                }
            }
            $ExcludedFromUserCount = ($ConditionalAccessPolicy.conditions.users.excludeUsers).count

            if ("GuestsOrExternalUsers" -in $ConditionalAccessPolicy.conditions.users.excludeUsers){
                $ExcludedFromUserCount--
            }

            #If only assigned/excluded from a group we will show the group display name, otherwise the number of groups assigned/excluded.
            if(($AssignedToGroup.count -gt 0) -and ($AssignedToUserCount -gt 0)){
                $outAssignedToGroup = "$AssignedToUserCount user(s), " + $AssignedToGroup.count + " group(s)"
            } elseif(($AssignedToGroup.count -eq 0) -and ($AssignedToUserCount -gt 0)) {
                $outAssignedToGroup = "$AssignedToUserCount user(s)"
            } elseif(($AssignedToGroup.count -gt 0) -and ($AssignedToUserCount -eq 0)){
                $outAssignedToGroup = "" + $AssignedToGroup.count + " group(s)"
            } else {
                $outAssignedToGroup = "None"
            }

            if(($ExcludedFromGroup.count -gt 0) -and ($ExcludedFromUserCount -gt 0)){
                $outExcludedFromGroup = "$ExcludedFromUserCount user(s), " + $ExcludedFromGroup.count + " group(s)"
            } elseif(($ExcludedFromGroup.count -eq 0)  -and ($ExcludedFromUserCount -gt 0)) {
                $outExcludedFromGroup = "$ExcludedFromUserCount user(s)"
            } elseif(($ExcludedFromGroup.count -gt 0) -and ($ExcludedFromUserCount -eq 0)){
                $outExcludedFromGroup = "" + $ExcludedFromGroup.count + " group(s)"
            } else {
                $outExcludedFromGroup = "None"
            }

            $PolicyState = $ConditionalAccessPolicy.State
            if ($PolicyState -eq "enabledForReportingButNotEnforced") {
                $PolicyState = "ReportOnly"
            }
                
            #region 2: Assignment > Cloud apps or actions > Cloud Apps
            #Exchange 00000002-0000-0ff1-ce00-000000000000
            #SharePoint 00000003-0000-0ff1-ce00-000000000000
            #Teams cc15fd57-2c6c-4117-a88c-83b1d56b4bbe
            $ID = 2
            $Setting = "CloudApps"
            $SettingDescription = "Assignment > Cloud apps or actions > Cloud Apps"
            $Comment = ""
            $hasExchange = $false
            $hasSharePoint = $false
            $hasTeams = $false
            $hasOffice365 = $false
            $CloudAppValue = ""
            foreach ($Application in $ConditionalAccessPolicy.Conditions.Applications.IncludeApplications) {
                $appDisplayName = ($ServicePrincipals |  Where-Object -Property AppId -eq -Value $Application).DisplayName
                switch ($Application) {
                    "All" { $hasOffice365 = $true; $CloudAppValue = "All" }
                    "Office365" { $hasOffice365 = $true; $CloudAppValue = "Office 365" }
                    "00000002-0000-0ff1-ce00-000000000000" { $hasExchange = $true; $CloudAppValue += $appDisplayName + "; " }
                    "00000003-0000-0ff1-ce00-000000000000" { $hasSharePoint = $true; $CloudAppValue += $appDisplayName + "; "}
                    "cc15fd57-2c6c-4117-a88c-83b1d56b4bbe" { $hasTeams = $true; $CloudAppValue += $appDisplayName + "; " }
                    default { $CloudAppValue += $appDisplayName + "; " }
                }
            }
            if ($CloudAppValue.EndsWith("; ")) {
                $CloudAppValue = $CloudAppValue.Substring(0, $CloudAppValue.Length - 2)
            }

            if(((($AssignedToGroup.count -gt 0) -and ($hasOffice365 -or $hasTeams) -and ($PolicyState -NE "disabled")) -and (!$userExcluded) -and $userIncluded) -or $all){

                if (($hasExchange -and $hasSharePoint -and $hasTeams) -or ($hasOffice365)) {
                    $Status = "Supported"
                } else {
                    $Status = "Unsupported"
                    $Comment = "Teams Devices needs to access: Office 365 or Exchange Online, SharePoint Online, and Microsoft Teams"
                    $PolicyErrors++
                }

                $SettingPSObj = [PSCustomObject]@{
                    PolicyName              = $ConditionalAccessPolicy.displayName
                    PolicyState             = $PolicyState
                    Setting                 = $Setting 
                    Value                   = $SettingValue
                    TeamsDevicesStatus      = $Status 
                    Comment                 = $Comment
                    SettingDescription      = $SettingDescription 
                    AssignedToGroup         = $outAssignedToGroup
                    ExcludedFromGroup       = $outExcludedFromGroup 
                    AssignedToGroupList     = $AssignedToGroup
                    ExcludedFromGroupList   = $ExcludedFromGroup
                    PolicyID                = $ConditionalAccessPolicy.id
                    ID                      = $ID
                }
                $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceConditionalAccessPolicyDetailed')
                [void]$output.Add($SettingPSObj)
                #endregion

                #region 6: Assignment > Conditions > Locations
                $ID = 6.1
                $Setting = "includeLocations"
                $SettingDescription = "Assignment > Conditions > Locations"
                $Comment = ""
                $Status = "Supported"
                if ($ConditionalAccessPolicy.conditions.locations.includeLocations) {
                    $SettingValue = $ConditionalAccessPolicy.conditions.locations.includeLocations
                } else{
                    $SettingValue = "Not Configured"
                }
                $SettingPSObj = [PSCustomObject]@{
                    PolicyName              = $ConditionalAccessPolicy.displayName
                    PolicyState             = $PolicyState
                    Setting                 = $Setting 
                    Value                   = $SettingValue
                    TeamsDevicesStatus      = $Status 
                    Comment                 = $Comment
                    SettingDescription      = $SettingDescription 
                    AssignedToGroup         = $outAssignedToGroup
                    ExcludedFromGroup       = $outExcludedFromGroup 
                    AssignedToGroupList     = $AssignedToGroup
                    ExcludedFromGroupList   = $ExcludedFromGroup
                    PolicyID                = $ConditionalAccessPolicy.id
                    ID                      = $ID
                }
                $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceConditionalAccessPolicyDetailed')
                [void]$output.Add($SettingPSObj)

                $ID = 6.2
                $Setting = "excludeLocations"
                $SettingDescription = "Assignment > Conditions > Locations"
                $Comment = ""
                $Status = "Supported"
                if ($ConditionalAccessPolicy.conditions.locations.excludeLocations) {
                    $SettingValue = $ConditionalAccessPolicy.conditions.locations.excludeLocations
                } else{
                    $SettingValue = "Not Configured"
                }
                $SettingPSObj = [PSCustomObject]@{
                    PolicyName              = $ConditionalAccessPolicy.displayName
                    PolicyState             = $PolicyState
                    Setting                 = $Setting 
                    Value                   = $SettingValue
                    TeamsDevicesStatus      = $Status 
                    Comment                 = $Comment
                    SettingDescription      = $SettingDescription 
                    AssignedToGroup         = $outAssignedToGroup
                    ExcludedFromGroup       = $outExcludedFromGroup 
                    AssignedToGroupList     = $AssignedToGroup
                    ExcludedFromGroupList   = $ExcludedFromGroup
                    PolicyID                = $ConditionalAccessPolicy.id
                    ID                      = $ID
                }
                $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceConditionalAccessPolicyDetailed')
                [void]$output.Add($SettingPSObj)
                #endregion

                #region 7: Assignment > Conditions > Client apps
                $ID = 7
                $Setting = "ClientAppTypes"
                $SettingDescription = "Assignment > Conditions > Client apps"
                $Comment = ""
                foreach ($ClientAppType in $ConditionalAccessPolicy.Conditions.ClientAppTypes) {
                    if ($ClientAppType -eq "All") {
                        $Status = "Supported"
                        $Comment = ""
                    }
                    else {
                        $Status = "Unsupported"
                        $Comment = $URLTeamsDevicesCA
                        $PolicyErrors++
                    }
                    $SettingPSObj = [PSCustomObject]@{
                        PolicyName              = $ConditionalAccessPolicy.displayName
                        PolicyState             = $PolicyState
                        Setting                 = $Setting 
                        Value                   = $SettingValue
                        TeamsDevicesStatus      = $Status 
                        Comment                 = $Comment
                        SettingDescription      = $SettingDescription 
                        AssignedToGroup         = $outAssignedToGroup
                        ExcludedFromGroup       = $outExcludedFromGroup 
                        AssignedToGroupList     = $AssignedToGroup
                        ExcludedFromGroupList   = $ExcludedFromGroup
                        PolicyID                = $ConditionalAccessPolicy.id
                        ID                      = $ID
                    }
                    $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceConditionalAccessPolicyDetailed')
                    [void]$output.Add($SettingPSObj)
                }
                #endregion

                #region Assignment > Conditions > Filter for devices
                $ID = 8
                $Setting = "deviceFilter"
                $SettingDescription = "Assignment > Conditions > Filter for devices"
                $Comment = ""
                if ($ConditionalAccessPolicy.conditions.devices.deviceFilter.mode -eq "exclude") {
                        $Status = "Supported"
                        $SettingValue = $ConditionalAccessPolicy.conditions.devices.deviceFilter.mode + ": " + $ConditionalAccessPolicy.conditions.devices.deviceFilter.rule
                } else {
                    $SettingValue = "Not Configured"
                    $Status = "Warning"
                    $Comment = "https://learn.microsoft.com/microsoftteams/troubleshoot/teams-rooms-and-devices/teams-android-devices-conditional-access-issues"
                }
                $SettingPSObj = [PSCustomObject]@{
                    PolicyName              = $ConditionalAccessPolicy.displayName
                    PolicyState             = $PolicyState
                    Setting                 = $Setting 
                    Value                   = $SettingValue
                    TeamsDevicesStatus      = $Status 
                    Comment                 = $Comment
                    SettingDescription      = $SettingDescription 
                    AssignedToGroup         = $outAssignedToGroup
                    ExcludedFromGroup       = $outExcludedFromGroup 
                    AssignedToGroupList     = $AssignedToGroup
                    ExcludedFromGroupList   = $ExcludedFromGroup
                    PolicyID                = $ConditionalAccessPolicy.id
                    ID                      = $ID
                }
                $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceConditionalAccessPolicyDetailed')
                [void]$output.Add($SettingPSObj)
                #endregion

                #region 10: Access controls > Grant
                foreach ($BuiltInControl in $ConditionalAccessPolicy.GrantControls.BuiltInControls) {
                    $Comment = "" 
                    $SettingValue = "Enabled"
                    switch($BuiltInControl){
                            "mfa" { 
                                $ID = 11
                                $Status = "Warning"
                                $SettingDescription = "Access controls > Grant > Require multi-factor authentication"
                                $PolicyWarnings++
                                $Comment = "Require multi-factor authentication only supported for Teams Phones and Displays." 
                            }
                            "compliantDevice" {
                                $ID = 12
                                $Status = "Supported"
                                $SettingDescription = "Access controls > Grant > Require device to be marked as compliant"
                            }
                            "DomainJoinedDevice" { 
                                $ID = 13
                                $Status = "Unsupported"
                                $SettingDescription = "Access controls > Grant > Require Hybrid Azure AD joined device"
                                $PolicyErrors++
                            }
                            "ApprovedApplication" { 
                                $ID = 14
                                $Status = "Unsupported"
                                $SettingDescription = "Access controls > Grant > Require approved client app"
                                $Comment = $URLTeamsDevicesCA
                                $PolicyErrors++
                            }
                            "CompliantApplication" { 
                                $ID = 15
                                $Status = "Unsupported"
                                $SettingDescription = "Access controls > Grant > Require app protection policy"
                                $Comment = $URLTeamsDevicesCA
                                $PolicyErrors++
                            }
                            "PasswordChange" { 
                                $ID = 16
                                $Status = "Unsupported"
                                $SettingDescription = "Access controls > Grant > Require password change"
                                $Comment = $URLTeamsDevicesCA 
                                $PolicyErrors++
                            }
                            default { 
                                $ID = 10
                                $SettingDescription = "Access controls > Grant > " + $BuiltInControl
                                $Status = "Supported"}
                    }
                    $SettingPSObj = [PSCustomObject]@{
                        PolicyName              = $ConditionalAccessPolicy.displayName
                        PolicyState             = $PolicyState
                        Setting                 = $Setting 
                        Value                   = $SettingValue
                        TeamsDevicesStatus      = $Status 
                        Comment                 = $Comment
                        SettingDescription      = $SettingDescription 
                        AssignedToGroup         = $outAssignedToGroup
                        ExcludedFromGroup       = $outExcludedFromGroup 
                        AssignedToGroupList     = $AssignedToGroup
                        ExcludedFromGroupList   = $ExcludedFromGroup
                        PolicyID                = $ConditionalAccessPolicy.id
                        ID                      = $ID
                    }
                    $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceConditionalAccessPolicyDetailed')
                    [void]$output.Add($SettingPSObj) 
                }
                #endregion
                
                #region 17: Access controls > Grant > Custom Authentication Factors
                $ID = 17
                $Setting = "CustomAuthenticationFactors"
                $SettingDescription = "Access controls > Grant > Custom Authentication Factors"
                if ($ConditionalAccessPolicy.GrantControls.CustomAuthenticationFactors) {
                    $Status = "Unsupported"
                    $SettingValue = "Enabled"
                    $PolicyErrors++
                    $Comment = $URLTeamsDevicesCA
                } else {
                    $Status = "Supported"
                    $SettingValue = "Disabled"
                }
                $SettingPSObj = [PSCustomObject]@{
                    PolicyName              = $ConditionalAccessPolicy.displayName
                    PolicyState             = $PolicyState
                    Setting                 = $Setting 
                    Value                   = $SettingValue
                    TeamsDevicesStatus      = $Status 
                    Comment                 = $Comment
                    SettingDescription      = $SettingDescription 
                    AssignedToGroup         = $outAssignedToGroup
                    ExcludedFromGroup       = $outExcludedFromGroup 
                    AssignedToGroupList     = $AssignedToGroup
                    ExcludedFromGroupList   = $ExcludedFromGroup
                    PolicyID                = $ConditionalAccessPolicy.id
                    ID                      = $ID
                }
                $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceConditionalAccessPolicyDetailed')
                [void]$output.Add($SettingPSObj)
                #endregion

                #region 18: Access controls > Grant > Terms of Use
                $ID = 18
                $Setting = "TermsOfUse"
                $SettingDescription = "Access controls > Grant > Terms of Use"
                $Comment = "" 
                if ($ConditionalAccessPolicy.GrantControls.TermsOfUse) {
                    $Status = "Warning"
                    $SettingValue = "Enabled"
                    $Comment = $URLTeamsDevicesKnownIssues
                    $PolicyWarnings++
                } else {
                    $Status = "Supported"
                    $SettingValue = "Disabled"
                }
                $SettingPSObj               = [PSCustomObject]@{
                    PolicyName              = $ConditionalAccessPolicy.displayName
                    PolicyState             = $PolicyState
                    Setting                 = $Setting 
                    Value                   = $SettingValue
                    TeamsDevicesStatus      = $Status 
                    Comment                 = $Comment
                    SettingDescription      = $SettingDescription 
                    AssignedToGroup         = $outAssignedToGroup
                    ExcludedFromGroup       = $outExcludedFromGroup 
                    AssignedToGroupList     = $AssignedToGroup
                    ExcludedFromGroupList   = $ExcludedFromGroup
                    PolicyID                = $ConditionalAccessPolicy.id
                    ID                      = $ID
                }
                $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceConditionalAccessPolicyDetailed')
                [void]$output.Add($SettingPSObj)
                #endregion

                #region 19: Access controls > Session > Use app enforced restrictions
                $ID = 19
                $Setting = "ApplicationEnforcedRestrictions"
                $SettingDescription = "Access controls > Session > Use app enforced restrictions"
                $Comment = "" 
                if ($ConditionalAccessPolicy.SessionControls.ApplicationEnforcedRestrictions) {
                    $Status = "Unsupported"
                    $SettingValue = "Enabled"
                    $PolicyErrors++
                } else {
                    $Status = "Supported"
                    $SettingValue = "Disabled"
                }
                $SettingPSObj = [PSCustomObject]@{
                    PolicyName              = $ConditionalAccessPolicy.displayName
                    PolicyState             = $PolicyState
                    Setting                 = $Setting 
                    Value                   = $SettingValue
                    TeamsDevicesStatus      = $Status 
                    Comment                 = $Comment
                    SettingDescription      = $SettingDescription 
                    AssignedToGroup         = $outAssignedToGroup
                    ExcludedFromGroup       = $outExcludedFromGroup 
                    AssignedToGroupList     = $AssignedToGroup
                    ExcludedFromGroupList   = $ExcludedFromGroup
                    PolicyID                = $ConditionalAccessPolicy.id
                    ID                      = $ID
                }
                $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceConditionalAccessPolicyDetailed')
                [void]$output.Add($SettingPSObj)
                #endregion
                
                #region 19: Access controls > Session > Use Conditional Access App Control
                $ID = 19
                $Setting = "CloudAppSecurity"
                $SettingDescription = "Access controls > Session > Use Conditional Access App Control"
                $Comment = "" 
                if ($ConditionalAccessPolicy.SessionControls.CloudAppSecurity) {
                    $Status = "Unsupported"
                    $SettingValue = $ConditionalAccessPolicy.SessionControls.CloudAppSecurity.cloudAppSecurityType
                    $PolicyErrors++
                } else {
                    $Status = "Supported"
                    $SettingValue = "Not Configured"
                }
                $SettingPSObj = [PSCustomObject]@{
                    PolicyName              = $ConditionalAccessPolicy.displayName
                    PolicyState             = $PolicyState
                    Setting                 = $Setting 
                    Value                   = $SettingValue
                    TeamsDevicesStatus      = $Status 
                    Comment                 = $Comment
                    SettingDescription      = $SettingDescription 
                    AssignedToGroup         = $outAssignedToGroup
                    ExcludedFromGroup       = $outExcludedFromGroup 
                    AssignedToGroupList     = $AssignedToGroup
                    ExcludedFromGroupList   = $ExcludedFromGroup
                    PolicyID                = $ConditionalAccessPolicy.id
                    ID                      = $ID
                }
                $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceConditionalAccessPolicyDetailed')
                [void]$output.Add($SettingPSObj)
                #endregion

                #region 20: Access controls > Session > Sign-in frequency
                $ID = 20
                $Setting = "SignInFrequency"
                $SettingDescription = "Access controls > Session > Sign-in frequency"
                $Comment = "" 
                if ($ConditionalAccessPolicy.SessionControls.SignInFrequency.isEnabled -eq "true") {
                    $Status = "Warning"
                    $SettingValue = "" + $ConditionalAccessPolicy.SessionControls.SignInFrequency.Value + " " + $ConditionalAccessPolicy.SessionControls.SignInFrequency.Type
                    $Comment = "Users will be signout from Teams Device every " + $ConditionalAccessPolicy.SessionControls.SignInFrequency.Value + " " + $ConditionalAccessPolicy.SessionControls.SignInFrequency.Type
                    $PolicyWarnings++
                } else {
                    $Status = "Supported"
                    $SettingValue = "Not Configured"
                }
                $SettingPSObj = [PSCustomObject]@{
                    PolicyName              = $ConditionalAccessPolicy.displayName
                    PolicyState             = $PolicyState
                    Setting                 = $Setting 
                    Value                   = $SettingValue
                    TeamsDevicesStatus      = $Status 
                    Comment                 = $Comment
                    SettingDescription      = $SettingDescription 
                    AssignedToGroup         = $outAssignedToGroup
                    ExcludedFromGroup       = $outExcludedFromGroup 
                    AssignedToGroupList     = $AssignedToGroup
                    ExcludedFromGroupList   = $ExcludedFromGroup
                    PolicyID                = $ConditionalAccessPolicy.id
                    ID                      = $ID
                }
                $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceConditionalAccessPolicyDetailed')
                [void]$output.Add($SettingPSObj)
                #endregion

                #region 21: Access controls > Session > Persistent browser session
                $ID = 21
                $Setting = "PersistentBrowser"
                $SettingDescription = "Access controls > Session > Persistent browser session"
                $Comment = "" 
                if ($ConditionalAccessPolicy.SessionControls.PersistentBrowser.isEnabled -eq "true") {
                    $Status = "Unsupported"
                    $SettingValue = $ConditionalAccessPolicy.SessionControls.persistentBrowser.mode
                    $PolicyErrors++
                } else {
                    $Status = "Supported"
                    $SettingValue = "Not Configured"
                }
                
                $SettingPSObj = [PSCustomObject]@{
                    PolicyName              = $ConditionalAccessPolicy.displayName
                    PolicyState             = $PolicyState
                    Setting                 = $Setting 
                    Value                   = $SettingValue
                    TeamsDevicesStatus      = $Status 
                    Comment                 = $Comment
                    SettingDescription      = $SettingDescription 
                    AssignedToGroup         = $outAssignedToGroup
                    ExcludedFromGroup       = $outExcludedFromGroup 
                    AssignedToGroupList     = $AssignedToGroup
                    ExcludedFromGroupList   = $ExcludedFromGroup
                    PolicyID                = $ConditionalAccessPolicy.id
                    ID                      = $ID
                }
                $SettingPSObj.PSObject.TypeNames.Insert(0, 'TeamsDeviceConditionalAccessPolicyDetailed')
                [void]$output.Add($SettingPSObj)
                #endregion

                if ($PolicyErrors -gt 0) {
                    $StatusSum = "Has " + $PolicyErrors + " unsupported settings."
                    $displayWarning = $true
                }
                elseif ($PolicyWarnings -gt 0) {
                    $StatusSum = "Has " + $PolicyWarnings + " settings that may impact users."
                    $displayWarning = $true
                }
                else {
                    $StatusSum = "All settings supported."
                }
                    $PolicySum = [PSCustomObject]@{
                        PolicyID                = $ConditionalAccessPolicy.id
                        PolicyName              = $ConditionalAccessPolicy.DisplayName
                        PolicyState             = $PolicyState
                        AssignedToGroup         = $outAssignedToGroup
                        AssignedToGroupList     = $AssignedToGroup
                        ExcludedFromGroup       = $outExcludedFromGroup 
                        ExcludedFromGroupList   = $ExcludedFromGroup
                        TeamsDevicesStatus      = $StatusSum
                }
                $PolicySum.PSObject.TypeNames.Insert(0, 'TeamsDeviceConditionalAccessPolicy')
                [void]$outputSum.Add($PolicySum)
            } else {
                $skippedCAPolicies++
            }
        }
        if($totalCAPolicies -eq 0){
            if($UserUPN){
                Write-Warning ("The user " + $UserUPN + " doesn't have any Compliance Policies assigned.")
            } else {
                Write-Warning "No Conditional Access Policies assigned to All Users, All Devices or group found. Please use Test-UcTeamsDevicesConditionalAccessPolicy -IgnoreAssigment to check all policies."
            }
        }
        
        if($IncludeSupported -and $Detailed)
        {
            $output | Sort-Object PolicyName, ID
        } elseif ($Detailed) {
            if ((( $output | Where-Object -Property TeamsDevicesStatus -NE -Value "Supported").count -eq 0) -and !$IncludeSupported){
                Write-Warning "No unsupported settings found, please use Test-UcTeamsDevicesConditionalAccessPolicy -IncludeSupported, to output all settings."
            } else {
                $output | Where-Object -Property TeamsDevicesStatus -NE -Value "Supported" | Sort-Object PolicyName, ID
            }
        } else {
            if(($skippedCAPolicies -gt 0) -and !$All){
                Write-Warning ("Skipping $skippedCAPolicies conditional access policies since they will not be applied to Teams Devices")
                Write-Warning ("Please use the All switch to check all policies: Test-UcTeamsDevicesConditionalAccessPolicy -All")
            }
            if($displayWarning){
                Write-Warning "One or more policies contain unsupported settings, please use Test-UcTeamsDevicesConditionalAccessPolicy -Detailed to identify the unsupported settings."
            }
            $outputSum | Sort-Object PolicyName
        }
    }
}
#EndRegion '.\Public\Test-UcTeamsDevicesConditionalAccessPolicy.ps1' 697
#Region '.\Public\Test-UcTeamsOnlyModeReadiness.ps1' 0
<#
.SYNOPSIS
Check if a Tenant can be changed to TeamsOnly
 
.DESCRIPTION
This function will check if there is a lyncdiscover DNS Record that prevents a tenant to be switched to TeamsOnly.
 
.PARAMETER Domain
Specifies a domain registered with Microsoft 365
 
.EXAMPLE
PS> Test-UcTeamsOnlyModeReadiness
 
.EXAMPLE
PS> Test-UcTeamsOnlyModeReadiness -Domain uclobby.com
#>

Function Test-UcTeamsOnlyModeReadiness {
    Param(
        [Parameter(Mandatory = $false)]
        [string]$Domain
    )
    $outTeamsOnly = [System.Collections.ArrayList]::new()
    $connectedMSTeams = $false
    if ($Domain) {
        $365Domains = Get-UcM365Domains -Domain $Domain
    }
    else {
        try {
            $365Domains = Get-CsOnlineSipDomain
            $connectedMSTeams = $true
        }
        catch {
            Write-Error "Please Connect to before running this cmdlet with Connect-MicrosoftTeams"
        }
    }
    $DomainCount = ($365Domains.Count)
    $i = 1
    foreach ($365Domain in $365Domains) {
        $tmpDomain = $365Domain.Name
        Write-Progress -Activity "Teams Only Mode Readiness" -Status "Checking: $tmpDomain - $i of $DomainCount"  -PercentComplete (($i / $DomainCount) * 100)
        $i++
        $status = "Not Ready"
        $DNSRecord = ""
        $hasARecord = $false
        $enabledSIP = $false
        
        # 20220522 - Skipping if the domain is not SIP Enabled.
        if (!($connectedMSTeams)) {
            try {
                Invoke-WebRequest -Uri ("https://webdir.online.lync.com/AutoDiscover/AutoDiscoverservice.svc/root?originalDomain=" + $tmpDomain) | Out-Null
                $enabledSIP = $true
            }
            catch {
 
                if ($Error[0].Exception.Message -like "*404*") {
                    $enabledSIP = $false
                }
            }
        }
        else {
            if ($365Domain.Status -eq "Enabled") {
                $enabledSIP = $true
            }
        }
        if ($enabledSIP) {
            $DiscoverFQDN = "lyncdiscover." + $365Domain.Name
            $FederationFQDN = "_sipfederationtls._tcp." + $365Domain.Name
            $DNSResultA = (Resolve-DnsName $DiscoverFQDN -ErrorAction Ignore -Type A)
            $DNSResultSRV = (Resolve-DnsName $FederationFQDN -ErrorAction Ignore -Type SRV).NameTarget
            foreach ($tmpRecord in $DNSResultA) {
                if (($tmpRecord.NameHost -eq "webdir.online.lync.com") -and ($DNSResultSRV -eq "sipfed.online.lync.com")) {
                    break
                }
        
                if ($tmpRecord.Type -eq "A") {
                    $hasARecord = $true
                    $DNSRecord = $tmpRecord.IPAddress
                }
            }
            if (!($hasARecord)) {
                $DNSResultCNAME = (Resolve-DnsName $DiscoverFQDN -ErrorAction Ignore -Type CNAME)
                if ($DNSResultCNAME.count -eq 0) {
                    $status = "Ready"
                }
                if (($DNSResultCNAME.NameHost -eq "webdir.online.lync.com") -and ($DNSResultSRV -eq "sipfed.online.lync.com")) {
                    $status = "Ready"
                    $DNSRecord = $DNSResultCNAME.NameHost
                }
                else {
                    $DNSRecord = $DNSResultCNAME.NameHost
                }
                if ($DNSResultCNAME.Type -eq "SOA") {
                    $status = "Ready"
                }
            }
            $Validation = New-Object -TypeName PSObject -Property @{
                DiscoverRecord   = $DNSRecord
                FederationRecord = $DNSResultSRV
                Status           = $status
                Domain           = $365Domain.Name
            }

        }
        else {
            $Validation = New-Object -TypeName PSObject -Property @{
                DiscoverRecord   = ""
                FederationRecord = ""
                Status           = "Not SIP Enabled"
                Domain           = $365Domain.Name
            }
        }
        $Validation.PSObject.TypeNames.Insert(0, 'TeamsOnlyModeReadiness')
        $outTeamsOnly.Add($Validation) | Out-Null
    }
    return $outTeamsOnly
}
#EndRegion '.\Public\Test-UcTeamsOnlyModeReadiness.ps1' 117