Are your Powershell scripts taking too long to run and always timeout? PowerShell is often used to automate tasks or to configure systems such as Exchange. However, the use of Powershell is sometimes very resource or memory intensive, e.g. for file operations. The execution of Exchange cmdlets for the administration of Exchange OnPrem and Exchange Online/M365 can also result in the PowerShell process using a lot of memory. The memory used can be in the gigabyte range.
Typical error handling and its pitfalls
If you close the PowerShell process, (e.g. by closing the PowerShell shell, PowerShell console), the occupied RAM should quickly be released. But what happens if we execute further PowerShell commands or complex scripts that call various cmdlets in the same PowerShell process? Then the memory consumption continues to increase, which leads to the system becoming slower and slower. Incidentally, this behaviour not only affects Windows systems, but also Linux and macOS. However, Linux and macOS are only supported from PowerShell version 6 onwards.
So how can we free up RAM?
As far as I know, there are 3 ways to do this, which can also be combined with each other.
Index
Use the Garbage collectors
DPowerShell is based on the .NET Common Language Runtime (CLR). This means that, depending on the operating system, all or almost all options of the .NET Runtime are also available under PowerShell.
One of the advantages of .NET Runtime is that it automatically frees up RAM that is no longer required and avoid PowerShell timeout. This is done by the Garbage Collector (GC).
However, we have no direct influence on the automatic clean-up, i.e. this can take place immediately or at a later stage.
If we want to request the clean-up of the memory in its scripts immediately, we call the command “[System.GC]::Collect();”.
For further information on the garbage collector, the Microsoft documentation is helpful: https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/
Redirection of expenses to $null
Many PowerShell cmdlets return an output that is passed on to the PowerShell host, usually the PowerShell console. This also leads to further memory usage. If the output is not required, we can redirect it to $null.
There are 3 options for this:
Use of the Cmdlets Out-Null
The Out-Null cmdlet forwards an output to NULL. This is deleted from the pipeline and is also not displayed.
1 |
Connect-ExchangeOnline | Out-Null |
You can find further information at Microsoft: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/out-null?view=powershell-7.4
Redirection to $null
To redirect an output to NULL, the redirection operator “>” can also be used.
1 |
Connect-ExchangeOnline $null |
Assignment to $null
A third option is that we assign the results of a command to $null.
1 |
$null= Connect-ExchangeOnline |
Delete variables that are no longer required
As in other programming languages, we often use variables in PowerShell scripts. Memory is also reserved for variables and their content. We can also free up RAM when the variables are no longer required and avoid PowerShell timeout.
We can use the “Remove-Variable” or “Clear-Variable” cmdlets to do this, or we can simply assign the value $null to the variable.
However, there is also a special feature here, as we need to know.
The following situation should serve as an example:
A variable $text with the content “Hello world” is defined.
1 |
$text = „Hallo Welt“ |
Set the variable to $null
We can set a variable to null by writing:
1 |
$text = $null |
The special feature here is that we do not delete the variable. We only delete the content of the variable. The variable itself and therefore its memory reservation remains.
We can check this by entering the Get-Variabletext command. The variable name is written without the leading $.
If we execute this command, we get:
Using the Clear-Variable cmdlet
We can also use the Clear-Variable cmdlet to delete the value of the variable. However, the variable itself is also retained here.
1 |
Clear-Variabletext |
The variable name is also written here without the leading $.
Using the Remove-Variable cmdlet
The preferred way to delete a variable, and thus free up its RAM, is to use the Remove-Variable.
1 |
Remove-Variabletext |
The variable name is also written without the leading $.
If we now call the Get-Variable text command, an exception is thrown as the variable no longer exists in the system.
Avoid PowerShell timeout when accessing Exchange
To manage Exchange OnPrem or Exchange Online, PowerShell must also be used, as only a part can be managed via the administration interfaces.
Exchange Online
To connect to Exchange Online, a connection must first be established using the Cmdlet Connect-ExchangeOnline.
1 |
$null= Connect-ExchangeOnline |
(https://learn.microsoft.com/en-us/powershell/module/exchange/connect-exchangeonline?view=exchange-ps)
This cmdlet also returns an output that we can assign to $null if it is not needed. In addition, this cmdlet also creates tmpEXO files in the temp directory of the computer (%localappdata%\temp) on which the cmdlet was called.
If we frequently use Connect-ExchangeOnline in a script, new tmpEXO files are always created. To prevent this, we should always use
1 |
$null= Disconnect-ExchangeOnline -Confirm:$false |
With this cmdlet we end the Exchange session again.
(https://learn.microsoft.com/en-us/powershell/module/exchange/disconnect-exchangeonline?view=exchange-ps)
As we can see, the output of the cmdlet is again assigned to $null. The “-Confirm” parameter is only used to specify whether a confirmation prompt should be displayed or not. In the example above, no confirmation prompt is displayed.
Even if we do not want it, errors can occur when running PowerShell. If these occur, for example, after calling Connect-ExchangeOnline and no correct error handling has been implemented, the Exchange session remains open. This means that the temporary files are still available.
It therefore makes sense to place Connect-ExchangeOnline in a try..catch..finally statement. The call of Disconnect-ExchangeOnline should take place in the finally block, as this is always run through, i.e. even if an error occurs.
In the example, the call to Disconnect-ExchangeOnline was also placed in a separate try-catch statement. If an error occurs when calling Disconnect-ExchangeOnline, we can log this and prevent an uncontrolled cancellation of the code or script.
Exchange OnPrem
To connect remotely to Exchange OnPrem, a remote PowerShell session is used. To do this, the 3 cmdlets are called one after the other:
1 2 3 4 5 6 7 |
$credential= Get-Credential $psSession= New-PSSession -ConfigurationNameMicrosoft.Exchange -ConnectionUri http://ServerFQDN/PowerShell/ -Authentication Kerberos -Credential $credential Import-PSSession $psSession -DisableNameChecking |
The Exchange cmdlets can be used to control Exchange OnPrem.
If access to Exchange OnPrem is no longer required, we should release the session and also the variables from the memory.
The Remove-PSSession cmdlet is called to end the session:
1 |
Remove-PSSession -Id $psSession |
This cmdlet closes the current PowerShell session, stops all commands that are executed in this session, and releases the resources used. The variables used should also be deleted again using the Remove variable.
When using the Exchange Online and Exchange OnPrem cmdlets, it is also best to redirect all cmdlets that return an output and that we do not need to $null in order to save resources.
Conclusion
To avoid PowerShell timeout, resources must be saved. There are various ways to do this. Even though memory and hard disk space are relatively inexpensive these days, we should still try to use them carefully.
In this article, I have listed the ways of saving resources that I know and use myself. These can be used individually or in combination and are relatively straightforward to apply.
FirstAttribute AG – Identity Management & IAM Cloud Services
We would be happy to present our services and solutions to you. Get in touch and find out how we can help you.
Leave a Reply
<p>Your email is safe with us.<br/>Information about our <a href="https://activedirectoryfaq.com/contact-us/">data protection policies</a></p>