Teams PowerShell tool
Summary
At work we started to install Microsoft Teams machine wide installer on domain computers so that any user that would login would automatically get Teams on their profile.
I created a tool to make it easy to check if the machine wide version was installed and also to check if the user has logged into Teams. I also wanted to check if the prevent MSI installation registry value was present because that would stop the machine wide installation succeeding. I’ll explain how I get the information I need first and at the end I’ll give you the whole script.
Machine wide installer registry value
The first thing the tool checks is if Teams machine-wide installer is installed on the computer.
Test-Path 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{731F6BAA-A986-45A4-8936-7C3AAAAA760B}'This command will give you a false or true result.
Who’s logged in
The next thing the tool does is check who is logged into the computer either interactively or via remote desktop.
$Usr = Get-WmiObject -ComputerName $Computer -Class Win32_ComputerSystem -ErrorAction stop | Select-Object -ExpandProperty UserNameIf this command returns a result I use split and then choose the second item in the array which is the username after the backslash as I didn’t need the domain name from the SAMAccountName
$usr = $usr.split("{\}")
$UserName = $Usr[1]If the above command doesn’t return anything the tool will check if a user is connected via remote desktop, I explain that in the next section.
If the user is logged in remotely
I put the following command inside a function called Get-RDPUser which would be called from the main function for the tool.
$text = qwinsta.exe /server:$ComputerName | Select-String -Pattern 'Active'The above command finds a pattern with the word active as you will find this next to the username currently logged on. For example
Regex fun
In the next section I remove the part where it says rdp-tcp and any trailing numbers with a regex statement.
$text = $text.toString()
$text = $text -replace 'rdp-tcp#\d', ' '
#Remove beginning of string before username
Then I remove the number and the word active from the end of the string by replacing any white space with \s+, the number with \d, the space just before Active with \s+ and finally the word active with \w+. I hope that makes sense.
$text = $text -replace '\s+\d\s+\w+', ' ' #remove end of string after username Finally I use the trim method of string to remove any whitespace
$text = $text.trim() #Remove whitespace from beginning and endCheck if user has signed into Teams
This part requires two tasks, get the users SID (Security Identifier) and load users registry hive. I used this as a way to know if the user got Teams installed in their profile, but I guess if the user hasn’t bothered to sign into Teams it can be misleading, but it was useful as part of the tool. I suppose there could be a way of checking the logs to see if it installed for the user.
The HKEY_users registry hive contains user specific configuration information. Each key located under the HKEY_users hive is named after the SID of users that have signed into the computer. These keys are not available using the Registry PSProvider therefore I needed to create a new PSDrive to access the keys in this hive.
Get the user’s SID from Active Directory
After I get the user that’s logged in, it’s time to get the SID because I’ll need it to load the registry hive for that user. I use WMI even though CIM has replaced it, I haven’t bothered to go back and update it. I used the made up domain of ordobott.
$strSID = (Get-WmiObject -class Win32_UserAccount -Filter "Domain = 'ordobott' AND Name = '$UserName'").SIDSearch user registry hive for the HomeUserUpn
The following command simply creates a new PSDrive which you can then access using HKU:\. I pipe that to Out-Null because I don’t want the output that you get when you create a new PSDrive.
New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_Users | Out-NullThen the tool then checks the users registry for the Teams key. Since I use the following command in an invoke-command I need to reference the strSID which was outside of this scope be using the $using scope identifier. $using allows you to use a local variable in remote commands. I am only interested in the HomeUserUpn so I just use the expandproperty parameter to output just the value of that property and the rest of the properties that are output from the Get-ItemProperty command such as the PSParentPath and PSChildname.
Get-ItemProperty -path "HKU:\$using:strSID\software\Microsoft\Office\Teams\" -name HomeUserUpn -ErrorAction SilentlyContinue | Select-Object -ExpandProperty HomeUserUpnPrevent Installation from MSI registry key
Similar to the previous command we use the users SID to load the users hive but this time we look for the key PreventInstallationFromMsi. I didn’t want an error if not key was found so I used the parameter ErrorAction with the value SilentlyContinue
Get-ItemProperty -path "HKU:\$using:strSID\Software\Microsoft\Office\teams\" -name PreventInstallationFromMsi -ErrorAction SilentlyContinueThe output
At the end of the function I would just output a custom object with all the properties I had previously stored in variables.
$properties = @{ComputerName = $Computer
MachineWideInstaller = $MachineWideInstaller
Username = $UserName
HomeUserUpn = $HomeUserUpn
PreventInstallationFromMsi = $PreventInstallationFromMsi
}
$obj = New-Object -Property $properties -TypeName psobject
Write-Output $objThe function
I made the function into a module which would make it easier to use. As you can see below I like to add a comment with the function name or description (eg. #if/else) on the closing bracket of any if statements, functions or other blocks so I know what it belongs to.
[CmdletBinding()]
param ()
function Get-RDPUser {
[CmdletBinding()]
param (
[Parameter()]
[string]
$ComputerName
)
Write-Verbose "Using Qwinsta on $computername"
$text = qwinsta.exe /server:$ComputerName | Select-String -Pattern 'Active'
if ($text) {
$text = $text.toString()
#Remove beggining of string before username
$text = $text -replace 'rdp-tcp#\d', ' '
#remove end of string after username
$text = $text -replace '\s+\d\s+\w+', ' '
#Remove whitepspace from beginning and end
$text = $text.trim()
return $text
} else {
Write-Verbose "`$text is empty"
}
#If/else
} #Get-RDPUser
function Get-TeamsInstallStatus {
<#
.SYNOPSIS
Gets information from Teams
.DESCRIPTION
Gets the currently logged in user and gets the SID. With the user SID it checks the current user registry hive to check if they are logged into Teams.
Also checks if the system wide installer and the registry value to prevent MSI installation are present
.EXAMPLE
PS C:\> Get-TeamsInstallStatus -computername sysw10m38
Using it for a specfici computer
.EXAMPLE
PS C:\> Get-ADGroupMember -Identity TeamsInstall | select -Property @{l='computername';e={$_.name}} | Get-TeamsInstallStatus
You can pipe computers from a AD group
.INPUTS
Inputs (if any)
.OUTPUTS
Output (if any)
.NOTES
General notes
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
[Alias("Name")]
[string[]]
$ComputerName
)
begin {
}
process {
foreach ($Computer in $ComputerName) {
try {
Write-Verbose "`$Computer is $Computer"
Write-Verbose "Checking Teams Machine-Wide Installer"
$MachineWideInstaller = Invoke-command -ComputerName $Computer -ErrorAction stop -ScriptBlock {
Write-Verbose "Machine-Wide Installer Status:"
Test-Path 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{731F6BAA-A986-45A4-8936-7C3AAAAA760B}'
} #invoke-command
Write-Verbose "`$MachineWideInstaller is $MachineWideInstaller"
Write-Verbose "Getting Signed in User"
$Usr = Get-WmiObject -ComputerName $Computer -Class Win32_ComputerSystem -ErrorAction stop | Select-Object -ExpandProperty UserName
if ($usr) {
$usr = $usr.split("{\}")
write-verbose "$`Usr: $usr"
$UserName = $Usr[1]
}
else {
Write-Verbose "User not logged in console, checking rdp using qwinsta"
$UserName = Get-RDPUser -ComputerName $Computer
} #if/else
if ($username) {
Write-Verbose "Getting SID using WMI"
$strSID = (Get-WmiObject -class Win32_UserAccount -Filter "Domain = 'ordobott' AND Name = '$UserName'").SID
Write-Verbose "`$strSID: $strSID"
Write-Verbose "Checking if Installed for Users"
$HomeUserUpn = Invoke-Command -ComputerName $Computer -ScriptBlock {
New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_Users | Out-Null
Get-ItemProperty -path "HKU:\$using:strSID\software\Microsoft\Office\Teams\" -name HomeUserUpn -ErrorAction SilentlyContinue | Select-Object -ExpandProperty HomeUserUpn
} #invoke-command
if ($HomeUserUpn -eq '') {
$HomeUserUpn = $null
}
Write-Verbose "`$HomeUserUpn is $HomeUserUpn"
Write-Verbose "Checking if PreventInstallationFromMsi exists"
$PreventInstallationFromMsi = Invoke-Command -ComputerName $Computer -ScriptBlock {
New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_Users | out-null
Get-ItemProperty -path "HKU:\$using:strSID\Software\Microsoft\Office\teams\" -name PreventInstallationFromMsi -ErrorAction SilentlyContinue
} #invoke-command
} else {
Write-Verbose "`$UserName is null"
$HomeUserUpn = $null
$PreventInstallationFromMsi = $null
} #if/else
$properties = @{ComputerName = $Computer
MachineWideInstaller = $MachineWideInstaller
Username = $UserName
HomeUserUpn = $HomeUserUpn
PreventInstallationFromMsi = $PreventInstallationFromMsi
}
$obj = New-Object -Property $properties -TypeName psobject
Write-Output $obj
}
catch {
Write-Verbose "Cannot get MS Teams info"
$properties = @{ComputerName = $Computer
MachineWideInstaller = $null
Username = $null
HomeUserUpn = $null
PreventInstallationFromMsi = $null
}
$obj = New-Object -Property $properties -TypeName psobject
Write-Output $obj
} #try/catch
} #foreach
} #process
end {
} #end
} #function
