Today I’m sharing a useful bit of PowerShell I gracelessly punt from script to script whenever I need to make sure a prerequisite it met before running something and to keep checking until it’s met, then run what I need: “do X when Y is ready and keep checking Y until it’s ready”.
The original use for this was my script to create a new Microsoft 365 user, but hold off on some parts of it – such as time zone settings – until the Exchange Online mailbox is provisioned. That takes some time, so I wanted to keep checking and as soon as I could, continue the script.
Another scenario this could potentially be used is if you have to execute something with a dependency to connectivity to a remote server, but you need to wait on a connection being established, e.g. VPN connection separate to the script.
Here’s what it looks like using my “don’t proceed until the mailbox exists” example:
1 2 3 4 5 6 7 8 9 10 |
if ((Get-Mailbox $email).PrimarySmtpAddress -eq $email) { [System.Windows.Forms.MessageBox]::Show("Mailbox provisioned in Exchange Online.") } else { Do { Start-Countdown -Seconds 30 -Message 'Syncing to Azure AD and creating Office 365 mailbox.' } Until ((Get-Mailbox $email).PrimarySmtpAddress -eq $email) [System.Windows.Forms.MessageBox]::Show("Mailbox provisioned in Exchange Online.") } |
I can hear some screaming in the distance at my use of MessageBox, but for everyone else: what’s happening here?
First, Get-Mailbox runs against the mailbox we are interested in setting. It does not find the mailbox, so passes it to else, which says “I’ll wait a minute, then try again”, when until confirms the mailbox exists. And repeat. In your own use of checking the mailbox existed, all you’d need to do is change the variable of $email and the messages displayed.
I am also using Start-Countdown, an awesome function by Martin Pugh which is like Start-Sleep but with a visual countdown. Available here.
In another example, I only run the $install block if Test-Connection confirms I can connect to $server. This one uses Start-Sleep instead of Start-Countdown and consequently, if it’s running interactively, the user will see error messages until the server’s available. I use this when installing apps using PowerShell running as SYSTEM. For example, run at startup but only when the VPN’s connected.
1 2 3 4 5 6 7 8 9 10 |
if (Test-Connection "$Server" -Count 1 -TimeToLive 135){ &$install } else { Do { Start-Sleep -Seconds 30 } Until (Test-Connection "$Server" -Count 1 -TimeToLive 135) &$install } |
The template minus any example information is below, where Check-This presenents your cmdlet for a prerequisite, and Do-This is the cmdlet for your action.
1 2 3 4 5 6 7 8 9 10 |
if (Check-This){ Do-This } else { Do { Start-Sleep -Seconds 5 } Until (Check-This) Do-This } |
Nice idea, tough the construct can be simplified with a simple while() block which makes the if/else obsolete because the condition is checked before entering the loop.
+ would add some kind of maximum retry counter to avoid a script which never terminates.
E.g.
$retries =0
while( Get-Mailbox … -and $retries -le 5)
Nice one, much better solution.