Work

Streamdeck button to set Teams Status.

I’ve been on a mission to update my Teams status through a script for a long time and stumbled upon the stack overflow post where Monty had “looked” at the Teams code to get the parameters that it *currently* supports.

By using the multiaction switch block I was able to have a button that toggles between Available and Away with my StreamDeck.

This is a hack right now and needs polishing but @PatchandSwitch wanted documentation.

Create a powershell script file that runs teams with the appropriate parameter, one file for each status.

Note that the file path can and will change. I found that attempting a dir -recurse of c:\program files\windowsapps fails so unfortunately it’s a hard coded filename for now.

set-teamsaway.ps1

&”C:\Program Files\WindowsApps\MSTeams_24074.2321.2810.3500_x64__8wekyb3d8bbwe\ms-teams.exe” –set-presence-to-away

Create a set-teamsaway.bat that calls this powershell script.

powershell -noprofile <path to script files>\set-teamsaway.ps1

Repeat this process but use set-teamsavailable.ps1 and set-teamsavailable.bat

Use any of the following options.

–set-presence-to-available
–set-presence-to-dnd
–set-presence-to-be-right-back
–set-presence-to-away
–set-presence-to-offline
–reset-presence

In the Stream Deck Editor, Create a Multi Action Switch and drag to an empty (if you have one) spot.

Select the content action

Drag a System/Open button to the first tab and set the Title and App. I had to use the bat file because powershell would not work for me.

Select the second tab at the top and repeat but use the set-teamsavailable.bat

Now if you press the button, it will set you to away, press it again and it will set you back to available again.

Streamdeck is now finally working with Teams

Screenshot of Elgato's Streamdeck buttons showing microphone, blur plugins and camera view

TLDR – set the api key whilst having a meeting with yourself

I was fortunate to get a Streamdeck for a birthday present back in March this year – the announcement that it was integrated into Teams was the final push to get one. I had been debating whether to get one or not and had been using TouchPortal as a software solution running on an old Kindle as test bed for quite some time to see how much I would use this functionality. The answer is a lot!

(Very) Shortly after getting the device and setting it up, the Teams plugin was pulled from the store but eventually re-released and it has never worked since. If I was lucky it would intermittently work for 5 minutes or even 20 if there was a blue moon, I was wearing my lucky jumper and there was a z in the day’s name.

Other people have also had the same problem and there has been a long thread on the elgato subreddit about the issue with various solutions provided but none of them have worked for me, at least on a consistent basis.

However a post yesterday on the Microsoft community forums had someone getting the plugin working with the new Teams version. I followed the steps and the issue was still not working for me – however it did give me a clue.

This morning I confirmed that the WebSocket was listening on port 8124 with netstat -aon | find “8124” and was fiddling around trying to get the plugin to work. I had already confirmed the api key (found from Settings/Privacy in Teams and pasted it into the plugin settings and was still getting the lock icons, but this morning I selected the calendar, “Meet now” option and started a meeting with myself. (It was very early and there was no one else to call!)

After the meeting was established, I opened the plugin and re-entered the api key in the button settings and this time the setting worked, all of my buttons had the padlock overlay removed.

Fearing the worst, I killed the meeting, restarted Teams and Stream Deck, and all the buttons are still working. Being the optimist, I then rebooted the computer, restarted Teams and Stream Deck and the buttons still work. 2 hours later and the buttons are *still* working.

For those interested, the TL, TR, BL, BR refer to the monitors attached to the desktop computer and enable me to quickly switch the OBS camera to the relevant monitor. Using the classic Teams app allows me to share a video feed from a camera so all I have to do is share the one video stream and let OBS and Streamdeck take care of everything else. Unfortunately, the New Teams does not have this functionality and the OBS output screen needs to be shared which requires way more key presses to setup and the OBS output running on one of the monitors, so until this shows up in the New Teams application, it’s Teams Classic for me.

Solved: “No row was found with id” when selecting row from Excel with Power Automate

I’ve been trying to automate the manipulation of incoming emails from an unnamed source that contains the users first and last name but not their email address and then send an email to that person.

Unfortunately, I am not able to compose an email address based on the first letter of their first name and full surname along with the domains as the domains could be different (and the naming convention too).

I was initially doing this with a case statement in Power Automate which is very ugly, inefficient and pretty laborious to set up and I also found out the hard way that there is a limit to the number of Case statements you can have – 25 in case (pun intended) you are wondering.

Don’t do this – it’s ugly, inefficient and hard to manage.

Spent some time today to switch to an excel lookup that will grab the first and last name from the email and then pull back the email address that should be used in Excel. However, it kept returning the error no row was found with id ‘First Last’ even though there was only one row with the value of ‘First Last’

The issue was that I had created a table in Excel for all of Column C through E and this includes lots of empty lines that the Get a row command will choke on. Fixed by defining the table in Excel to only contain the actual data that I needed such as $C$1:$E$80, Power Automate now only returns one value and the script continues on successfully.

Power Automate flow to read email, set some variables and send an email out

Reinstalling Connectwise Automate (Labtech) on Linux machine after cloning a disk in Azure

Connectwise Automate / Labtech agent icon

This is a pretty basic post and doesn’t go into the requirements to do a Linux version of sysprep, but I needed to temporarily clone a RedHat server in Azure that had the Connectwise Automate agent on it this morning. After a reboot, the server was showing up in Automate as the same id as the previous server which is not what we needed.

The documentation for the Linux agent is pretty lacking and restarting agents did not seem to fix the issue.

By changing into the location of the Automate agent, you can run the uninstaller.sh app and then reinstall with a new installer.sh that is typically going to be already downloaded to your home/ltechagent directory. Poking around it was easiest to remove and readd as a new agent.

Documenting as I’m sure I or other people will need this again.

cd /usr/local/ltechagent
sudo ./uninstaller.sh
cd ..
ls  -al
#No lttechagent directory should exist anymore

#change to location where labtech installer was downloaded, typically in the home directory and already unzipped.
cd ~/ltechagent
chmod +x install.sh
sudo ./install.sh
ps -ef | grep ltech
#Should see two results of the agent running
#View results of install
cat /tmp/ltech_install_log.txt
#refresh Automate console to see machine showing up correctly

Using Microsoft Graph Update-MgGroup with Certificate Based Authentication as a working alternative for Set-UnifiedGroup

Yesterday I had a fun time converting a PowerShell script that used the set-unifiedgroup powershell command that was originally running with basic authentication, to a script that would run using certificate based authentication so it can run against a tenant that uses Modern Authentication for office365.

There were quite a few hurdles to overcome so hopefully this helps other people.

Step 1 – Fix Office365 logins

Blank browser page that loads when attempting to log into Office365

The computer was initially generating a blank white page on the screen when attempting to log into the Office365 or Azure portal. This was resolved by resetting the browser settings in Control Panel / Internet Options. Clearing cookies and temporary internet files did not help for this step.

Step2 – Connect to Office365.

The ExchangeOnline module was already installed on the computer, but needed to be updated with

 update-Module -Name ExchangeOnlineManagement

The rest of this phase reduced the previous 5+ lines of code to log into Office365 to a one liner. There are quite a few steps involved in this, but App-only authentication in Exchange Online PowerShell and Security & Compliance PowerShell | Microsoft Learn is a good document to follow. Make sure that the SSL certificate is documented somewhere so that you get a reminder *before* the certificate expires. Once the certificate is uploaded to Azure and permissions are set it is possible to connect with

connect-exchangeonline -CertificateThumbprint "1a2b3c4d5e6f....." -appid "123abc-456def...." -organization "companyname.onmicrosoft.com"

Initially I thought that would be it, but after running the script I discovered the next big snag –

Step3 – Converting set-unifiedgroup to MS Graph module equivalent

The script gathered a list of Microsoft 365 distribution groups (or UnifiedGroups) in Office365 that had their access level not set to private and changed them to be private. The previous code was this

Get-UnifiedGroup -ResultSize Unlimited | Where-Object { $_.primarySmtpAddress -match 'companyname.onmicrosoft.com' -and $_.accesstype -ne 'Private' -and $_.HiddenFromAddressListsEnabled -ne 'true' } | Set-UnifiedGroup -AccessType Private -HiddenFromAddressListsEnabled $true -verbose

However, set-unifiedgroup is one of a few commands that cannot be used with Certificate Based Authentication and the msgraph module has to be used instead. The Microsoft documentation points this out but does not provide helpful information on how to actually get this accomplished. The linked information refers to api calls rather than using actual Microsoft Graph PowerShell commands and there were several gotchas in this process (hence this blog post).

First the Microsoft graph module needs to be installed on the computer with

Install-Module Microsoft.Graph

Step3a – Importing Microsoft.Graph.Groups

When importing the Microsoft.Graph module on the machine into PowerShell , not only did it take over 10 minutes to import, an error message is generated stating

Import-Module : Function Get-MgUserContactFolderChildFolderContactMultiValueExtendedProperty cannot be created because<br>function capacity 4096 has been exceeded for this scope.
Screenshot error stating Import-Module : Function Get-MgUserContactFolderChildFolderContactMultiValueExtendedProperty cannot be created because
function capacity 4096 has been exceeded for this scope.

This is due to the sheer number of commands available in the module and PowerShell 5.1 has a limit to the number of commands that can be used. Using PowerShell 7 is one way of fixing that issue but I was trying to reduce the amount of changes being made to this pc. By importing a subsection of the module with import-module microsoft.graph.groups the number of commands that are available is greatly reduced and the import is also a lot quicker. 10 seconds to run instead of over 10 minutes from above.

Step3b – Filter left

The original search returned all groups in Office365 and then filtered them locally. In this large organization, there are a significant number of groups returned and the lookup took several minutes to run. By passing a filter parameter I was able to reduce the number of groups significantly.

Unfortunately the filter parameter does not support accesstype or the primarysmtpaddress so these have to be filtered out by piping the groups to a select-object statement.

Get-UnifiedGroup -ResultSize Unlimited | Where-Object { $_.primarySmtpAddress -match 'companyname.onmicrosoft.com' -and $_.accesstype -ne 'Private' -and $_.HiddenFromAddressListsEnabled -ne 'true' }

becomes

$groups=get-unifiedgroup -filter {(hiddenfromaddresslistsenabled -ne $true)} | where-object {$_.primarysmtpaddress -match 'companyname.onmicrosoft.com' -and $_.accesstype -ne 'Private'} 

After making this change, the groups were returned in less than a minute.

Step3c – Changing from set-unifiedgroup to Update-MgGroup

The new command to modify the group is update-mggroup but the standard command does not have the ability to change the access type as per the documentation at Update-MgGroup (Microsoft.Graph.Groups) | Microsoft Learn

Screenshot for the update-MgGroup command that is missing the AccessType parameter

However, after switching the document to the Beta version of the api in the dropdown menu on the left, the accesstype parameter becomes available. The above screenshot shows AccessType is missing vs the screenshot below that includes this option.

Screenshot of the update-MgGroup beta command that includes the AccessType parameter

We therefore end up with the following commands to set the profile to Beta, load the Microsft.Graph.Groups subset of the module and then connect to Microsoft. Graph

Select-MgProfile -Name "beta"
import-module microsoft.graph.groups
connect-mggraph -clientid "1a2b3c4d5e-1234-1a2c3-a1aa-aa12b3456c7d" -tenantid companyname.onmicrosoft.com -certificatethumbprint "0a4f....fe"

I had issues with the next bit of code and ended up using my Exchange online connections to retrieve the groups that matched the criteria of not being hidden from the address list and where the access type was not private, and then using the ExternalDirectoryObjectID from those groups in the update-mggroup command from the graph module.

In theory I should have been able to search using the get-mggroup command but I was having issues getting the filters and search to work correctly. I could not get any results to come back for searches that included the companyname.onmicrosoft.com email address. As soon as I added that to the criteria the results came back empty. Due to time constraints I used the (admittedly) messy option of using Exchange to grab the groups and graph to update them.

$groups=get-unifiedgroup -filter {(hiddenfromaddresslistsenabled -ne $true)} | where-object {$_.primarysmtpaddress -match ‘companyname.onmicrosoft.com’ -and $_.accesstype -ne ‘Private’}

foreach ($group in $groups) {
update-mggroup -groupid $group.ExternalDirectoryObjectId -accesstype "Private" -HideFromAddressLists
}

Finally, the script was completed by disconnecting the Graph and the Exchange connections

get-pssession | Remove-PSSession
Disconnect-MgGraph
Stop-Transcript

Using the transcript option at the beginning of the script really helped in debugging this code as it’s meant to run from a scheduled task and viewing the transcript enabled me to see what the issue was and then decide to test further in an interactive session.

SQL, dbatools and Webroot

I have been busy working on a SQL server migration, and have come across a couple of issues.

Firstly, attempting to install or upgrade an SQL instance with Webroot on the machine generates an unauthorized action on the machine. Reviewing the error logs provides the following error

Exception type: Microsoft.SqlServer.Configuration.Sco.ScoException
Message: 
Attempted to perform an unauthorized operation.
HResult : 0x84bb0001
FacilityCode : 1211 (4bb)
ErrorCode : 1 (0001)
Data: 
WatsonData = Uninstall@{145996FC-8E6B-47AB-BEA5-A84F12B72AF5}

Navigating to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall registry shows the value {14599…..} is Webroot. Set server into unmanaged mode and then removing Webroot then enabled me to install SQL service packs.

I’ve also run into the same issue on new installs which leads me to the second issue.

I’m using dbatools to install with notes taken from the newly printed dbatools in a month of lunches. A book I purchased pre-pandemic and promptly forgot about but I finally got my hands on the book.

dbatools is a fantastic resource for SQL admins who want to automate everything and a common task is installing SQL.

Unfortunately there’s a typo in Listing 13.6 and 13.7 The parameter SQLUSERDBDATADIR that is coded into the sql config.ini file should actually be SQLUSERDBDIR

It took me a while to figure that one out. I then went to check out the books online only to find someone had found and reported the same error – yesterday!

The moral of the story is to check the books online first.

Also, whilst looking at my Manning books – I have a Powershell problem (or maybe with all these books I don’t!

Listing of Powershell books from Manning Publications

Fixed: 161008 The source virtual machine doesn’t have a network interface or all the network interfaces were deleted when using Azure Migrate

I’ve been working on an Azure Migrate project at work this week and had an interesting issue after attempting to start a replication of the source servers. The configurations were brought in from the Azure Migrate assessment tool but I received an odd error that there was no connected network interface on the source server.

This is a very strange error as all the source servers do have network interfaces otherwise there would not be much point in migrating them up to Azure! The error id 161008 and messages of “No connected network interface is configured for the virtual machine” and “The source virtual machine doesn’t have a network interface or all the network interfaces were deleted” did not make much sense, however the recommendation of “If there is no network interface on the source machine, add one and then go to Computer and Network settings of the virtual machine to configure the network interface” was a slight clue.

error id 161008 and messages of No connected network interface is configured for the virtual machine" and The source virtual machine doesn't have a network interface or all the network interfaces were deleted

Part of the solution steps imply that you create a nic on the server, but as the server has not actually been created in Azure yet, this step is not possible and the source server obviously has nic’s already setup so no change can be made on that server.

After getting a second pair of eyes on the issue (Thanks A!) , we had an Aha moment in the Compute and Network section of the server setup. The Assessment had set the nic’s on the machine to Do not Create and Secondary Network. As there was no primary nic configured on this page, the error message above is generated. Setting the Secondary nic to Primary rather than Secondary enabled the replication to start successfully.

Screenshot of configuring Compute and Network for an Azure migrate server

2 lessons from this – Always ensure you have a primary nic configured when using Azure Migrate. Get a second pair of eyes for that fresh look at the problem as sometimes you just can’t see the wood for the trees.

Fixed: Powershell prompts to run scripts when importing sessions – change %temp%

Powershell Security warning

My new work computer has had issues attempting to run Office365 commands for a while. After successfully connecting to Office365, using connect-exchangeonline (as an example), I would get a security warning – “Run only scripts that you trust. While scripts from the internet can be useful, this script can potentially harm your computer. If you trust this script, use the Unblock-File cmdlet to allow the script to run without this warning message. Do you want to run c:\temp\temp\tmp_rnncyvj4.v10\tmp_rnncyvj4.v10.format.ps1xml?
[D] Do not run [R] Run once [S] Suspend [?] Help (default is “D”):

And this would repeat with the appropriate .psm1 file too.

The usual solution is to use unblock-file or set-executionpolicy -remotesigned.

However, in this case the files are dynamically downloaded and will have a different filename everytime and setting the execution policy did not make any difference.

I ended up changing my temp folder from c:\temp\temp to c:\andy\temp and now I no longer get prompted.

Very odd behaviour that is not too annoying until you run scripts across all the office365 tenants!

Fixed: ScreenConnect / Control missing from Labtech / Automate

Automate screenshot

For the past two days my Automate window was missing all of the Screenconnect plugins that allow one click remote access to client machines. Both the one that shows at the top of the computer list and also when the machine window is launched. (Screenshot below shows how it should look)

Screenshot showing the control icon in Automate for computers

A reinstall of the software (including renaming the left over Labtech files in Program files and Program Data after removing the software) did not fix the issue.

However, reviewing the C:\ProgramData\LabTech Client\Logs\yyyymmdd_LTcErrors.txt showed lots of plugin exceptions including the following:-

An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch. See http://go.microsoft.com/fwlink/?LinkId=155569 for more information

Following that link provided the hint that loadFromRemoteSources needs to be enabled.

Editing “C:\Program Files (x86)\LabTech Client\LTClient.exe.config” and adding <loadFromRemoteSources enabled=”true”/> just before the /runtime> line, Automate now includes the control button.

LTClient config file showing the loadfromremotesources element

Fixed: Lastpass seems to randomly add incorrect data to Forms.

We use a web based documentation system at work and have had a couple of instances where data for companies (ie Company X) seems to have been randomly edited in forms to include data from another form (ie Company Y) in the system. In a form that had a username, password, url and notes field we discovered that a tech could go in and edit the notes (and only the notes field) and without realising it, the username and password were also being updated in the form. The tech would hit save and now the saved password was incorrect.
Thankfully the documentation system has revision histories to allow us to revert back to the previous settings. but it is still a painful process to go back and review recent changes to see which ones were genuine edits and which were changed incorrectly.

We initially blamed it on LastPass filling out data as the issue would not occur if we disabled LastPass, however a search in LastPass would not return the data that was being added to the form. It took us a while to track down, but Chris, one of our techs worked out what was going on.

Sample lastpass password screen with extra field button highlighted

LastPass has additional fields that don’t show up when you browse (and apparently search) and the data from these extra fields were automatically being filled in for some reason. Click the wrench, highlighted in the above screenshot to see the extra hidden fields.

Our solution was to delete these extra fields, save the record in LastPass and we no longer have LastPass corrupting our data.