14 min read
Graph PowerShell and Microsoft Teams – Part 3 of our series
Damian shares his knowledge about Microsoft Graph. His third article goes into detail about teams and introduces the...
ScriptRunner Blog
This article is not going to guide you through setting up PowerShell (PS) remoting since this has already been explained on this blog in an earlier post on Getting Started with PowerShell Remoting.
Instead, I’m going to show you ways to perform troubleshooting, remotely, for instance when the problem is that a GUI based logon such as through mstsc or the physical or virtual console hangs/goes slowly.
Ensure you have PS remoting setup to the machines that you are likely to need to troubleshoot, and test it, before the problems occur as by then it may well be too late.
A number of cmdlets take a -Computername argument and thus can be run against a remote machine or machines (depending on whether they take a string or string array type for this argument – see the screenshot below). Rather than doing a lot of RTFM’ing we can use PowerShell to query how many cmdlets take a -Computername argument:
Get-Command | Where { $_.Parameters -and $_.Parameters.ContainsKey('ComputerName') } | select Name,@{n='Array';e={$_.Parameters['ComputerName'].ParameterType.IsArray}},source
These generally do not use PS remoting to function but typically need firewall ports open (disabling Windows firewall on internal facing servers is not something I recommend!). Where an array can be passed, some will operate in parallel to some degree, meaning that the results can come back quicker than if each computer is queried separately. In PowerShell v7.1, only 20 cmdlets take a -Computername parameter and most of these are to do with remoting.
A command that can be self-remoted that I use a lot is Get-WinEvent because often the clues/answer to an issue is contained within an event log. Unlike in the days of old, when there were only four event logs, we now typically have in excess of 300 and manual review/searching of these is both tedious and time-consuming. This is where we can use PowerShell to retrieve the event logs from a problematic machine and either manually look through them or search for specific terms.
Here for instance is a one liner which will show all events that have occurred within a specified time range today (add the dd/mm/yyyy or mm/dd/yyyy to the time strings to search on another day) on the specified computer “fred”:
Get-WinEvent -ListLog * -ComputerName fred | Where-Object { $_.RecordCount } | ForEach-Object{ Get-WinEvent -ComputerName fred -EA SilentlyContinue -FilterHashtable @{logname=$_.logname;starttime='07:45:15';endtime='07:47:15'}} | Select-Object * -ExcludeProperty ?Id,Version,Qualifiers,Level,Task,OpCode,Keywords,Bookmark,*Ids,Properties | sort TimeCreated
Putting the results into a csv file (via Export-CSV, alias epcsv) or an on screen grid view (via Out-Gridview, alias ogv), makes it very easy to then search/filter on specific search terms.
Event logs in grid view
A common cause of failure for remoting Get-WinEvent is when the remote firewall does not have the “Remote Event Log Management” rule enabled or the account used to query the event logs does not have permission.
Read our two-part blog series to learn more about handling and monitoring events with PowerShell and .NET or WMI.
Where a cmdlet/function does not have intrinsic remoting, it can be remoted using PowerShell via the Invoke-Command cmdlet, assuming that PS remoting has been successfully configured and an account with sufficient privilege is available (by default only administrators can use PS remoting but members of the built-in local group “Remote Management Users” are also allowed).
Either run Invoke-Command as a user with PS remoting privileges or pass a credential object, easily constructed interactively via Get-Credential), via the -credential parameter. One of the fundamental principles of PowerShell is having objects on the pipeline so they can be subsequently processed by further cmdlets/functions/scripts and Invoke-Command honours this such that objects returned from remote machines can be processed on the local machine running the script/cmdlet.
Here is an example where we get the processes from a remote machine and see which have used the most CPU which can be a good indicator of what has caused performance issues – especially if you log on to a machine that has had problems only to find that at the time you check, CPU consumption is low.
List of machines, sorted by their CPU usage
Note that we have retrieved all processes from the remote machine but have sorted and selected the top ten consumers on the machine where we ran Invoke-Command. Depending on the operation, it can be best to filter at the remote end in order to reduce the amount of data that needs to be transferred and processed – one of the golden rules for best PowerShell performance is “filter as far left as you can”.
Also note that the -HideComputerName parameter was used to declutter the results, although that is generally not advised when Invoke-Command is passed an array of computer names so that it operates on multiple machines simultaneously otherwise knowing what result came from what computer may be tricky (if relevant).
Using Invoke-Command with the -Computername parameter establishes and tears down a new remote session for every invocation so where you are going to be doing a lot of remoting to the same machine(s) it becomes inefficient and takes more time and resource than establishing a single session and using that for every remote command and finally tearing it down once when everything is complete.
PowerShell provides this in the form of remote sessions which supports the above usage, non-interactively, or it can be used interactively as you would in the old (insecure) days of Unix when you might telnet to a machine to troubleshoot it using command line only (non GUI) tools and more recently, and much more securely, to do similar over an SSH connection with tools like PuTTY.
To use non-interactively, we establish the remote connection, once, with New-PSSession, which we assign to a variable that we can then pass to subsequent Invoke-Command calls and then finally pass to Remove-Session to tear it down.
Using PowerShell remote sessions non-interactively via New-PSSession
To establish and use a remote session interactively, we use Enter-PSSession thus:
Using PowerShell remote sessions interactively via Enter-PSSession
Notice how the prompt changes to have the computer name in square brackets to help you remember that you are now on a different machine and maybe avoid doing the wrong thing on the wrong machine (maybe!).
Command line history is stored on the local machine so even if the remote machine does not have a persistent user profile and you restart it, the commands previously used will be available next time you remote to it (and are searchable via ctrl r/s rather than having to cursor through them) and you also have a record of what was done (assuming you have a persistent user profile locally).
If you run a command that has a user interface, such as notepad, it will just hang, without showing a window. But, we can do things like start a remote SysInternals Process Monitor, headless, by using Start-Process to effectively run the process in the background. That way, we can stop the trace when the event we are troubleshooting, e.g. a slow Windows 10 logon, has completed, or we can pass the /runtime parameter to procmon and specify how long to run it for in seconds. A typical command line to run procmon headless in an already established remote session is:
Start-Process -FilePath c:\temp\Procmon.exe -ArgumentList ‘/accepteula /backingfile c:\temp\logon.pml /nofilter /quiet’
When we have captured what we need, we invoke procmon again with just the /terminate parameter to stop the trace and the .pml file(s) produced via the /backingfile argument can be examined where you have a GUI session. Note that by default, accessing network shares from within a remote session does not work, although it is possible to enable this ability.
Hopefully this has given you an idea of what is possible to achieve on remote systems with PowerShell and a few scenarios where using it can be quicker/easier than establishing a full remote session.
If you want to learn more about troubleshooting with PowerShell, make sure to check out my other article on My Top 10 PowerShell Commands for Troubleshooting Windows Problems.
Oct 30, 2024 by Damian Scoles
Damian shares his knowledge about Microsoft Graph. His third article goes into detail about teams and introduces the...
Oct 16, 2024 by Damian Scoles
Explore the evolving landscape of managing Exchange Online with Microsoft's Graph PowerShell module versus the...
Oct 8, 2024 by Damian Scoles
Users will encounter one or two hurdles when they start using Graph. Damian Scoles wrote three articles provides help...
Consultant, Software Developer, Troubleshooter. Current Citrix CTP, VMware vExpert, and Microsoft MVP. Inventor of AppSense Application Manager. PowerShell addict.