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' 47
#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
    )
    $regex = "^(.*@)(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})$"
    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"
    }

    foreach ($AllowedAudience in $AllowedAudiences) {
        $temp = [regex]::Match($AllowedAudience , $regex).captures.groups
        if ($temp.count -ge 2) {
            return   New-Object -TypeName PSObject -Property @{ TenantID = $temp[2].value }
        }
    }
}
#EndRegion '.\Public\Get-UcM365TenantId.ps1' 42
#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 DeviceId
Specifies the Teams Device ID
 
.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 -DeviceId 00000000-0000-0000-0000-000000000000
 
.EXAMPLE
PS> Get-UcTeamsDevice -Detailed
 
#>


$GraphURI_BetaAPIBatch = "https://graph.microsoft.com/beta/`$batch"
$GraphURI_Users = "https://graph.microsoft.com/v1.0/users/"

Function Get-UcTeamsDevice {
    Param(
        [Parameter(Mandatory = $false)]
        [ValidateSet("Phone","MTR","MTRA","MTRW","SurfaceHub","Display","Panel","SIPPhone")]
        [string]$Filter,
        [Parameter(Mandatory = $false)]
        [string]$DeviceId,
        [Parameter(Mandatory = $false)]
        [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"
    }

    if($DeviceId){

        $graphRequests =  [System.Collections.ArrayList]::new()
        $gRequestTmp = New-Object -TypeName PSObject -Property @{
            id = "device"
            method = "GET"
            url = "/teamwork/devices/"+$DeviceId
        }
        $graphRequests.Add($gRequestTmp) | Out-Null
        $gRequestTmp = New-Object -TypeName PSObject -Property @{
            id = "activity"
            method = "GET"
            url = "/teamwork/devices/"+$DeviceId+"/activity"
        }
        $graphRequests.Add($gRequestTmp) | Out-Null
        $gRequestTmp = New-Object -TypeName PSObject -Property @{
            id = "configuration"
            method = "GET"
            url = "/teamwork/devices/"+$DeviceId+"/configuration"
        }
        $graphRequests.Add($gRequestTmp) | Out-Null
        $gRequestTmp = New-Object -TypeName PSObject -Property @{
            id = "health"
            method = "GET"
            url = "/teamwork/devices/"+$DeviceId+"/health"
        }
        $graphRequests.Add($gRequestTmp) | Out-Null
        $gRequestTmp = New-Object -TypeName PSObject -Property @{
            id = "operations"
            method = "GET"
            url = "/teamwork/devices/"+$DeviceId+"/operations"
        }
        $graphRequests.Add($gRequestTmp) | Out-Null
        $graphBody = ' { "requests": '+ ($graphRequests | ConvertTo-Json) + ' }' 
        $graphResponses = (Invoke-MgGraphRequest -Method Post -Uri $GraphURI_BetaAPIBatch -Body $graphBody).responses

        $TeamsDevice = ($graphResponses | Where-Object{$_.id -eq "device"}).body
        $TeamsDeviceActivity = ($graphResponses | Where-Object{$_.id -eq "activity"}).body
        $TeamsDeviceConfiguration = ($graphResponses | Where-Object{$_.id -eq "configuration"}).body
        $TeamsDeviceHealth = ($graphResponses | Where-Object{$_.id -eq "health"}).body
        $TeamsDeviceOperations = ($graphResponses | Where-Object{$_.id -eq "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 = ""
        }

        if ($TeamsDevice.currentuser.id) {
            $userUPN = (Invoke-MgGraphRequest -Uri ($GraphURI_Users + $TeamsDevice.currentuser.id ) -Method GET).userPrincipalName
        }
        else {
            $userUPN = ""
        }

        $outTDObj = New-Object -TypeName PSObject -Property @{
            UserDisplayName = $TeamsDevice.currentuser.displayName
            UserUPN         = $userUPN 
    
            DeviceType      = Convert-UcTeamsDeviceType $TeamsDevice.deviceType
            DeviceID        = $DeviceId
            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 
        }
        $outTDObj.PSObject.TypeNAmes.Insert(0, 'TeamsDevice')
        return $outTDObj
    }
    else{
        $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
                            DeviceID        = $TeamsDevice.id
                            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
                            DeviceID        = $TeamsDevice.id

                            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' 460
#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
 
#>

Function Get-UcTeamsVersion {
    Param(
        [string]$Path,
        [string]$Computer
    )
    $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(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 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
            }
        }
    }
    return $outTeamsVersion
}
#EndRegion '.\Public\Get-UcTeamsVersion.ps1' 177
#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 all settings in each Intune Compliance Policy
 
.EXAMPLE
PS> Test-UcTeamsDevicesCompliancePolicy
 
.EXAMPLE
PS> Test-UcTeamsDevicesCompliancePolicy -Detailed
 
#>

Function Test-UcTeamsDevicesCompliancePolicy {

    Param(
        [Parameter(Mandatory = $false)]
        [switch]$Detailed,
        [Parameter(Mandatory = $false)]
        [string]$PolicyID,
        [Parameter(Mandatory = $false)]
        [string]$PolicyName
    )

    $connectedMSGraph = $false
    $CompliancePolicies = $null

    $scopes = (Get-MgContext).Scopes

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

    try {
        $CompliancePolicies = (Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/deviceManagement/deviceCompliancePolicies" -Method GET).value
        $connectedMSGraph = $true
    }
    catch {
        Write-Error 'Please connect to MS Graph with Connect-MgGraph -Scopes "DeviceManagementConfiguration.Read.All" before running this script'
    }

    if ($connectedMSGraph) {
        $output = [System.Collections.ArrayList]::new()
        $outputSum = [System.Collections.ArrayList]::new()
        foreach ($CompliancePolicy in $CompliancePolicies) {
            if ((($PolicyID -eq $CompliancePolicy.id) -or ($PolicyName -eq $CompliancePolicy.displayName) -or (!$PolicyID -and !$PolicyName)) -and ($CompliancePolicy."@odata.type" -eq "#microsoft.graph.androidCompliancePolicy")) {
                $PolicyErrors = 0
                $PolicyWarnings = 0

                $ID = 1
                $Setting = "deviceThreatProtectionEnabled"
                $Comment = ""
                if ($CompliancePolicy.deviceThreatProtectionEnabled) {
                    $Status = "Unsupported"
                    $PolicyErrors++
                }
                else {
                    $Status = "Supported"
                }

                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.deviceThreatProtectionEnabled
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null


                $ID = 3
                $Setting = "securityBlockJailbrokenDevices"
                $Comment = ""
                if ($CompliancePolicy.securityBlockJailbrokenDevices) {
                    $Status = "Warning"
                    $Comment = "This setting can cause sign in issues."
                    $PolicyWarnings++
                }
                else {
                    $Status = "Supported"
                }
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.securityBlockJailbrokenDevices
                    TeamsDevicesStatus = $Status
                    Comment            = $Comment 
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null  


                $ID = 4
                $Setting = "deviceThreatProtectionRequiredSecurityLevel"
                $Comment = ""
                if ($CompliancePolicy.deviceThreatProtectionRequiredSecurityLevel -ne "unavailable") {
                    $Status = "Unsupported"
                    $PolicyErrors++
                }
                else {
                    $Status = "Supported"
                }

                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.deviceThreatProtectionRequiredSecurityLevel
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null


                $ID = 5
                $Setting = "securityRequireGooglePlayServices"
                $Comment = ""
                if ($CompliancePolicy.securityRequireGooglePlayServices) {
                    $Status = "Unsupported"
                    $PolicyErrors++
                }
                else {
                    $Status = "Supported"
                }

                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $SettingPSObj
                    Value              = $CompliancePolicy.securityRequireGooglePlayServices
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')                            
                $output.Add($SettingPSO) | Out-Null
        

                $ID = 6
                $Setting = "securityRequireUpToDateSecurityProviders"
                $Comment = ""
                if ($CompliancePolicy.securityRequireUpToDateSecurityProviders) {
                    $Status = "Unsupported"
                    $Comment = "Google play isn't installed on Teams Android devices."
                    $PolicyErrors++
                }
                else {
                    $Status = "Supported"
                }

                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.securityRequireUpToDateSecurityProviders
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null

        
                $ID = 7
                $Setting = "securityRequireVerifyApps"
                $Comment = ""
                if ($CompliancePolicy.securityRequireVerifyApps) {
                    $Status = "Unsupported"
                    $PolicyErrors++
                }
                else {
                    $Status = "Supported"
                }

                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.securityRequireVerifyApps
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null


                $ID = 8.1
                $Setting = "securityRequireSafetyNetAttestationBasicIntegrity"
                $Comment = ""
                if ($CompliancePolicy.securityRequireSafetyNetAttestationBasicIntegrity) {
                    $Status = "Unsupported"
                    $PolicyErrors++
                }
                else {
                    $Status = "Supported"
                }

                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.securityRequireSafetyNetAttestationBasicIntegrity
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')                            
                $output.Add($SettingPSObj) | Out-Null


                $ID = 8.2
                $Setting = "securityRequireSafetyNetAttestationCertifiedDevice"
                $Comment = ""    
                if ($CompliancePolicy.securityRequireSafetyNetAttestationCertifiedDevice) {
                    $Status = "Unsupported"
                    $PolicyErrors++
                }
                else {
                    $Status = "Supported"
                }

                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.securityRequireSafetyNetAttestationCertifiedDevice
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null


                $ID = 9.1
                $Setting = "osMinimumVersion"
                $Comment = ""
                if ($CompliancePolicy.osMinimumVersion) {
                    $Status = "Warning"
                    $Comment = "This setting can cause sign in issues."
                    $PolicyWarnings++
                }
                else {
                    $Status = "Supported"
                }
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.osMinimumVersion
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null  

        
                $ID = 9.2
                $Setting = "osMaximumVersion"
                $Comment = ""
                if ($CompliancePolicy.osMaximumVersion) {
                    $Status = "Warning"
                    $Comment = "This setting can cause sign in issues."
                    $PolicyWarnings++
                }
                else {
                    $Status = "Supported"
                }
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.osMaximumVersion
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null  


                $ID = 10
                $Setting = "storageRequireEncryption"
                $Comment = ""
                if ($CompliancePolicy.storageRequireEncryption) {
                    $Status = "Warning"
                    $Comment = "https://docs.microsoft.com/en-us/microsoftteams/rooms/supported-ca-and-compliance-policies?tabs=phones#supported-device-compliance-policies"
                    $PolicyWarnings++
                }
                else {
                    $Status = "Supported"
                }

                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.storageRequireEncryption
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null


                $ID = 11
                $Setting = "securityPreventInstallAppsFromUnknownSources"
                $Comment = ""
                if ($CompliancePolicy.securityPreventInstallAppsFromUnknownSources) {
                    $Status = "Unsupported"
                    $PolicyErrors++
                }
                else {
                    $Status = "Supported"
                }

                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.securityPreventInstallAppsFromUnknownSources
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null    



                $ID = 15
                $Setting = "passwordMinutesOfInactivityBeforeLock"
                $Comment = ""
                if ($null -ne $CompliancePolicy.passwordMinutesOfInactivityBeforeLock) {
                    $Status = "Unsupported"
                    $PolicyErrors++
                }
                else {
                    $Status = "Supported"
                }
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.passwordMinutesOfInactivityBeforeLock
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null  


                $ID = 16
                $Setting = "passwordRequired"
                $Comment = ""
                if ($CompliancePolicy.passwordRequired) {
                    $Status = "Unsupported"
                    $PolicyErrors++
                }
                else {
                    $Status = "Supported"
                }
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.passwordRequired
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null  


                $ID = 17.1
                $Setting = "passwordRequiredType"
                $Comment = ""
                if ($CompliancePolicy.passwordRequiredType -ne 'deviceDefault') {
                    $Status = "Unsupported"
                    $PolicyErrors++
                }
                else {
                    $Status = "Supported"
                }
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.passwordRequiredType
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null  


                $ID = 17.2
                $Setting = "passwordMinimumLength"
                $Comment = ""
                if ($null -ne $CompliancePolicy.passwordMinimumLength) {
                    $Status = "Unsupported"
                    $PolicyErrors++
                }
                else {
                    $Status = "Supported"
                }
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.passwordMinimumLength
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null  

        
                $ID = 17.3
                $Setting = "passwordExpirationDays"
                $Comment = ""
                if ($null -ne $CompliancePolicy.passwordExpirationDays) {
                    $Status = "Unsupported"
                    $PolicyErrors++
                }
                else {
                    $Status = "Supported"
                }
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.passwordExpirationDays 
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null  


                $ID = 17.4
                $Setting = "passwordPreviousPasswordBlockCount"
                $Comment = ""
                if ($null -ne $CompliancePolicy.passwordPreviousPasswordBlockCount) {
                    $Status = "Unsupported"
                    $PolicyErrors++
                }
                else {
                    $Status = "Supported"
                }
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.passwordPreviousPasswordBlockCount
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null  


                $ID = 18
                $Setting = "minAndroidSecurityPatchLevel"
                $Comment = ""
                if ($CompliancePolicy.minAndroidSecurityPatchLevel -ne "") {
                    $Status = "Warning"
                    $Comment = "This setting can cause sign in issues."
                    $PolicyWarnings++
                }
                else {
                    $Status = "Supported"
                }

                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    ID                 = $ID
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    Setting            = $Setting
                    Value              = $CompliancePolicy.minAndroidSecurityPatchLevel
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicy')
                $output.Add($SettingPSObj) | Out-Null

                if ($PolicyErrors -gt 0) {
                    $StatusSum = "Unsupported"
                }
                elseif ($PolicyWarnings -gt 0) {
                    $StatusSum = "Warning"
                }
                else {
                    $StatusSum = "Supported"
                }

                $PolicySum = New-Object -TypeName PSObject -Property @{
                    PolicyID           = $CompliancePolicy.id
                    PolicyName         = $CompliancePolicy.displayName
                    TeamsDevicesStatus = $StatusSum
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceCompliancePolicySumary')
                $outputSum.Add($PolicySum) | Out-Null
            }
        }
        if ($Detailed) {
            $output | Sort-Object PolicyName, ID 
        }
        else {
            $outputSum 
        }
    }
}
#EndRegion '.\Public\Test-UcTeamsDevicesCompliancePolicy.ps1' 540
#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
 
.EXAMPLE
PS> Test-UcTeamsDevicesConditionalAccessPolicy
 
.EXAMPLE
PS> Test-UcTeamsDevicesConditionalAccessPolicy -Detailed
 
#>


Function Test-UcTeamsDevicesConditionalAccessPolicy {

    Param(
        [Parameter(Mandatory = $false)]
        [switch]$Detailed
    )

    $connectedMSGraph = $false
    $ConditionalAccessPolicies = $null
    $URLTeamsDevicesCA = "aka.ms/TeamsDevicesAndroidPolicies#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 )) {
        Connect-MgGraph -Scopes "Policy.Read.All"
    }

    try {
        $ConditionalAccessPolicies = (Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" -Method GET).Value
        $connectedMSGraph = $true
    }
    catch {
        Write-Error 'Please connect to MS Graph with Connect-MgGraph -Scopes "Policy.Read.All" before running this script'
    }

    if ($connectedMSGraph) {
        $output = [System.Collections.ArrayList]::new()
        $outputSum = [System.Collections.ArrayList]::new()
        foreach ($ConditionalAccessPolicy in $ConditionalAccessPolicies) {

            $CAPolicyState = $ConditionalAccessPolicy.State

            if ($CAPolicyState -eq "enabledForReportingButNotEnforced") {
                $CAPolicyState = "ReportOnly"
            }

            $PolicyErrors = 0
            $PolicyWarnings = 0

            #Cloud Apps
            #Exchange 00000002-0000-0ff1-ce00-000000000000
            #SharePoint 00000003-0000-0ff1-ce00-000000000000
            #Teams cc15fd57-2c6c-4117-a88c-83b1d56b4bbe
            $hasExchange = $false
            $hasSharePoint = $false
            $hasTeams = $false
            $hasOffice365 = $false
            $CloudAppValue = ""
            foreach ($Application in $ConditionalAccessPolicy.Conditions.Applications.IncludeApplications) {

                switch ($Application) {
                    "All" { $hasOffice365 = $true; $CloudAppValue = "All" }
                    "Office365" { $hasOffice365 = $true; $CloudAppValue = "Office 365" }
                    "00000002-0000-0ff1-ce00-000000000000" { $hasExchange = $true; $CloudAppValue += "Exchange, " }
                    "00000003-0000-0ff1-ce00-000000000000" { $hasSharePoint = $true; $CloudAppValue += "SharePoint, " }
                    "cc15fd57-2c6c-4117-a88c-83b1d56b4bbe" { $hasTeams = $true; $CloudAppValue += "Teams, " }
                    "None" { $CloudAppValue = "None" }
                }
            }
            if ($CloudAppValue.EndsWith(", ")) {
                $CloudAppValue = $CloudAppValue.Substring(0, $CloudAppValue.Length - 2)
            }

            if (($hasExchange -and $hasSharePoint -and $hasTeams) -or ($hasOffice365)) {
                $Status = "Supported"
                $Comment = ""
            }
            else {
                $Status = "Unsupported"
                $Comment = "Teams Devices needs to access: Office 365 or Exchange Online, SharePoint Online, and Microsoft Teams"
                $PolicyErrors++
            }
            
            $SettingPSObj = New-Object -TypeName PSObject -Property @{
                CAPolicyID         = $ConditionalAccessPolicy.id
                CAPolicyName       = $ConditionalAccessPolicy.displayName
                CAPolicyState      = $CAPolicyState
                Setting            = "Cloud Apps or actions"
                Value              = $CloudAppValue
                TeamsDevicesStatus = $Status 
                Comment            = $Comment
            }
            $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceConditionalAccessPolicy')
            $output.Add($SettingPSObj) | Out-Null

            #Conditions - ClientAppTypes
            foreach ($ClientAppType in $ConditionalAccessPolicy.Conditions.ClientAppTypes) {
                if ($ClientAppType -eq "All") {
                    $Status = "Supported"
                    $Comment = ""
                }
                else {
                    $PolicyErrors++
                    $Status = "Unsupported"
                    $Comment = $URLTeamsDevicesCA
                }

                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    CAPolicyID         = $ConditionalAccessPolicy.id
                    CAPolicyName       = $ConditionalAccessPolicy.displayName
                    CAPolicyState      = $CAPolicyState
                    Setting            = "Conditions - ClientAppTypes"
                    Value              = $ClientAppType
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceConditionalAccessPolicy')
                $output.Add($SettingPSObj) | Out-Null
            }

            #Device

            if ($ConditionalAccessPolicy.conditions.devices) {
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    CAPolicyID         = $ConditionalAccessPolicy.id
                    CAPolicyName       = $ConditionalAccessPolicy.displayName
                    CAPolicyState      = $CAPolicyState
                    Setting            = "Device Filter"
                    Value              = "Configured"
                    TeamsDevicesStatus = "Supported"
                    Comment            = $ConditionalAccessPolicy.conditions.devices.deviceFilter.mode + ": " + $ConditionalAccessPolicy.conditions.devices.deviceFilter.rule
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceConditionalAccessPolicy')
                $output.Add($SettingPSObj) | Out-Null
            }
            else {
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    CAPolicyID         = $ConditionalAccessPolicy.id
                    CAPolicyName       = $ConditionalAccessPolicy.displayName
                    CAPolicyState      = $CAPolicyState
                    Setting            = "Device Filter"
                    Value              = "Missing"
                    TeamsDevicesStatus = "Warning"
                    Comment            = "Device Filter is required when multiple Conditional Access policies exist."
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceConditionalAccessPolicy')
                $output.Add($SettingPSObj) | Out-Null
            }



            #Grant Controls

            $Setting = "GrantControls"
            foreach ($BuiltInControl in $ConditionalAccessPolicy.GrantControls.BuiltInControls) {
                $Comment = "" 
                if ($BuiltInControl -in 'DomainJoinedDevice', 'ApprovedApplication', 'CompliantApplication', 'PasswordChange') {
                    $PolicyErrors++
                    $Status = "Unsupported"
                    $Comment = $URLTeamsDevicesCA
                }
                else {
                    $Status = "Supported"
                }

                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    CAPolicyID         = $ConditionalAccessPolicy.id
                    CAPolicyName       = $ConditionalAccessPolicy.displayName
                    CAPolicyState      = $CAPolicyState
                    Setting            = $Setting 
                    Value              = $BuiltInControl 
                    TeamsDevicesStatus = $Status 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceConditionalAccessPolicy')
                $output.Add($SettingPSObj) | Out-Null
            }

            
            if ($ConditionalAccessPolicy.GrantControls.CustomAuthenticationFactors) {
                $PolicyWarnings++
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    CAPolicyID         = $ConditionalAccessPolicy.id
                    CAPolicyName       = $ConditionalAccessPolicy.displayName
                    CAPolicyState      = $CAPolicyState
                    Setting            = $Setting 
                    Value              = "CustomAuthenticationFactors"
                    TeamsDevicesStatus = "Unsupported"
                    Comment            = $URLTeamsDevicesCA
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceConditionalAccessPolicy')
                $output.Add($SettingPSObj) | Out-Null
            }

            if ($ConditionalAccessPolicy.GrantControls.TermsOfUse) {
                $PolicyWarnings++
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    CAPolicyID         = $ConditionalAccessPolicy.id
                    CAPolicyName       = $ConditionalAccessPolicy.displayName
                    CAPolicyState      = $CAPolicyState
                    Setting            = $Setting 
                    Value              = "Terms of Use"
                    TeamsDevicesStatus = "Warning"
                    Comment            = $URLTeamsDevicesKnownIssues
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceConditionalAccessPolicy')
                $output.Add($SettingPSObj) | Out-Null
            }

            $Setting = "SessionControls"
            $Comment = "" 
            if ($ConditionalAccessPolicy.SessionControls.ApplicationEnforcedRestrictions) {
                $PolicyErrors++
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    CAPolicyID         = $ConditionalAccessPolicy.id
                    CAPolicyName       = $ConditionalAccessPolicy.displayName
                    CAPolicyState      = $CAPolicyState
                    Setting            = $Setting 
                    Value              = "ApplicationEnforcedRestrictions"
                    TeamsDevicesStatus = "Unsupported" 
                    Comment            = $Comment
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceConditionalAccessPolicy')
                $output.Add($SettingPSObj) | Out-Null
            }
            if ($ConditionalAccessPolicy.SessionControls.CloudAppSecurity) {
                $PolicyErrors++
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    CAPolicyID         = $ConditionalAccessPolicy.id
                    CAPolicyName       = $ConditionalAccessPolicy.displayName
                    CAPolicyState      = $CAPolicyState
                    Setting            = $Setting 
                    Value              = "CloudAppSecurity"
                    TeamsDevicesStatus = "Unsupported" 
                    Comment            = $URLTeamsDevicesCA
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceConditionalAccessPolicy')
                $output.Add($SettingPSObj) | Out-Null
            }
            if ($ConditionalAccessPolicy.SessionControls.SignInFrequency) {
                $PolicyWarnings++
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    CAPolicyID         = $ConditionalAccessPolicy.id
                    CAPolicyName       = $ConditionalAccessPolicy.displayName
                    CAPolicyState      = $CAPolicyState
                    Setting            = $Setting 
                    Value              = "SignInFrequency"
                    TeamsDevicesStatus = "Warning" 
                    Comment            = "Users will be signout from Teams Device every " + $ConditionalAccessPolicy.SessionControls.SignInFrequency.Value + " " + $ConditionalAccessPolicy.SessionControls.SignInFrequency.Type
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceConditionalAccessPolicy')
                $output.Add($SettingPSObj) | Out-Null
            }
            if ($ConditionalAccessPolicy.SessionControls.PersistentBrowser) {
                $PolicyErrors++
                $SettingPSObj = New-Object -TypeName PSObject -Property @{
                    CAPolicyID         = $ConditionalAccessPolicy.id

                    CAPolicyName       = $ConditionalAccessPolicy.displayName
                    CAPolicyState      = $CAPolicyState
                    Setting            = $Setting 
                    Value              = "PersistentBrowser"
                    TeamsDevicesStatus = "Unsupported" 
                    Comment            = $URLTeamsDevicesCA
                }
                $SettingPSObj.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceConditionalAccessPolicy')
                $output.Add($SettingPSObj) | Out-Null
            }

            if ($PolicyErrors -gt 0) {
                $StatusSum = "Unsupported"
            }
            elseif ($PolicyWarnings -gt 0) {
                $StatusSum = "Warning"
            }
            else {
                $StatusSum = "Supported"
            }

            $PolicySum = New-Object -TypeName PSObject -Property @{
                CAPolicyID         = $ConditionalAccessPolicy.id
                CAPolicyName       = $ConditionalAccessPolicy.DisplayName
                CAPolicyState      = $CAPolicyState
                TeamsDevicesStatus = $StatusSum
            }
            $PolicySum.PSObject.TypeNAmes.Insert(0, 'TeamsDeviceConditionalAccessPolicySummary')
            $outputSum.Add($PolicySum) | Out-Null
        }
        if ($Detailed) {
            $output | Sort-Object CAPolicyName, Setting
        }
        else {
            $outputSum 
        }
    }
}
#EndRegion '.\Public\Test-UcTeamsDevicesConditionalAccessPolicy.ps1' 312
#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