Have a command that (on 2008 boxes) throws an exception due to the buffer limit.
Essentially, the script stops WAUA, blows out SoftwareDistribution, then reaches out to rebuild SD and check for updates against WSUS then checks back in so the alerts in WSUS stop.
I want a specific line to retry if an exception is thrown until it finishes without an exception, then we can tell it to report back in to WSUS to clear it out.
Stop-Service WUAUSERV
Remove-Item -Path C:\WINDOWS\SoftwareDistribution -recurse
Start-Service WUAUSERV
GPUpdate /force
WUAUCLT /detectnow
sleep 5
## This is the command I'd like to loop, if possible, when an exception is thrown ##
$updateSession = new-object -com "Microsoft.Update.Session"; $updates=$updateSession.CreateupdateSearcher().Search($criteria).Updates
WUAUCLT /reportnow
Any kind of help would be appreciated. Everything I've been able to find has been how to create my own exception but not how to handle it when an exception is thrown and retry until it finishes without an error.
Edit:
Then based on the below Answer, is this how I would want it to be written so it will continue to check until it runs successfully and then it'll report back in?
Stop-Service WUAUSERV
Remove-Item -Path C:\WINDOWS\SoftwareDistribution -recurse
Start-Service WUAUSERV
GPUpdate /force
WUAUCLT /detectnow
sleep 5
while(-not $?) {$updateSession = new-object -com "Microsoft.Update.Session"; $updates=$updateSession.CreateupdateSearcher().Search($criteria).Updates}
WUAUCLT /reportnow
You can use the special character $? This will return false if the last command returned with error, so your while loop would just look like:
while(-not $?)
See what is $? in powershell.
Alternatively, $error[0] gives the last thrown error message so you could build a while loop around that, similar to:
while($error[0] -ne "error message")
Set a value to false and only flip it if you get total success. Loop until you succeed or your timeout exceeds your defined value -- if you don't include a timeout, you have created an infinite loop condition where if the host never succeeds, it'll run until reboot.
$sts = $false
$count = 0
do {
try {
$updateSession = new-object -com "Microsoft.Update.Session"
$updates=$updateSession.CreateupdateSearcher().Search($criteria).Updates
$sts = $true
} catch {
## an exception was thrown before we got to $true
}
$Count++
Start-Sleep -Seconds 1
} Until ($sts -eq $true -or $Count -eq 100)
Related
I have two processes that reads from the same file. I want to make sure that those two processes doesn't run at the same. My approach is that , when a process starts, it should look for a file(process.txt). If the file doesn't exist, it creates the file and continue execution, reading from the shared file(sharedfile.txt) between the two processes. It then deletes the process.txt file after executing.
Since both process deletes process.txt file after execution, if the file exists,it should go into a start sleep until the other process finishes and deletes the file.
The problem here is when one process finishes and deletes the file(process.txt), the other still stay in the loop without executing even if no file exists. I am not sure of the right loop to use. I tried a couple of them and couldn't achieve my goal.
Clear Host
$sleeptime = 15
$lockfile = "C:\Users\processid.txt"
$file = test-path -Path $lockfile
Try
{
if($file -eq 'True')
{
echo “Lock file found!”
echo "This means file is being used by another process"
echo "Wait for file to be deleted/released"
echo “Sleeping for $sleeptime seconds”
Start-Sleep $sleeptime -Verbose
}
else
{
New-item -Path $lockfile
#Executing a code here
}
Remove-Item $lockfile –Force
}
}
Catch
{
Write-Host $_.Exception.Message`n
}
Consider this one, it creates a lock file and opens it for exclusive access. Let's you run your script, then cleans up afterwards. If access cannot be gained it waits 5 seconds before trying again.
Using this for both scripts should make them play nice and wait for their turn. If for some reason a lock file gets left behind (unplanned reboot that aborts script or similar) this one will still work fine as well.
$LockFile = 'C:\temp\test.lock'
# Loop that runs until we have exclusive write access to $LockFile
while ($FileStream.CanWrite -eq $false) {
if (-not (Test-Path -Path $LockFile)) {
Set-Content -Path $LockFile -Value 'Lockfile'
}
try {
$FileStream = [System.IO.File]::Open("C:\temp\test.lock",'Open','Write')
}
catch {
'Waiting 5 seconds'
Start-Sleep -Seconds 5
}
}
# ---
# Your actual script here
# ---
# Cleanup
$FileStream.Close()
$FileStream.Dispose()
Remove-Item -Path $LockFile -Force
You could also go one step further and instead lock the file you're actually using, but then you'll need to read it from the file stream since it will be locked for any cmdlets.
You could use a while loop like this
Clear Host
$sleeptime = 15
$lockfile = "C:\Users\processid.txt"
While(Test-Path -Path $lockfile){
Write-Host "Lock file found!"
Write-Host "This means file is being used by another process"
Write-Host "Wait for file to be deleted/released"
Write-Host "Sleeping for $sleeptime seconds"
Start-Sleep $sleeptime -Verbose
}
try{
New-item -Path $lockfile
#Executing a code here
}
catch{
Write-Host $_.Exception.Message`n
}
finally{
Remove-Item $lockfile –Force -ErrorAction SilentlyContinue
}
When I use start-process to create a new process and assign it to a variable...
$np = start-process -passThru notepad
... and then query .MainWindowHandle ...
$np.MainWindowHandle
... I seem to be given the HWND of the notepad.
However, when I try to do the same thing in one go...
(start-process -passThru notepad).MainWindowHandle
... I am given 0.
This is probably the case because MainWindowHandle is evaluated before notepad has created its window.
So, is there a way, without using start-sleep or going into a loop that repeadetly queries the value of MainWindowHandle, to wait until notepad is done starting up?
So, is there a way,
Yes
without using start-sleep or going into a loop that repeadetly queries
the value of MainWindowHandle
Not that I can think of :)
# Define a timeout threshold 10 seconds into the future
$threshold = (Get-Date).AddSeconds(10)
# Start the process
$proc = Start-Process notepad -PassThru
while(-not $proc.HasExited -and ((Get-Date) -lt $threshold -or $proc.MainWindowTitle -eq 0)){
Start-Sleep -MilliSeconds 250
}
if($proc.MainWindowTitle -eq 0){
if(-not $proc.HasExited)
$proc.Terminate()
}
throw 'Failed to spawn window in time'
return
}
# Do stuff with $proc.MainWindowHandle
As part of a Powershell script I need to perform a task that is typically quick, but can sometimes take a long amount of time. I want to execute the task, then wait either for it fo finish or for set time to pass, whichever happens first. When either condition happens, I need the Powershell script to return back to the command prompt.
Here is the closest I've come up with (using ping as an example)
$x = Start-Process -Filepath "ping" -ArgumentList 'google.com -n 100' -NoNewWindow -PassThru;
Start-Sleep -Seconds 5;
try { Stop-Process -Id $x.Id -ErrorAction stop } catch {};
This will kill the process after the timoeout is reached (if it is still running) and return back to the prompt. However, it won't return to the prompt if the command successfully completes before timeout. This results in the script always taking however long is specified in the timeout value.
The desired semantics are similar or identical to Linux's timeout command.
Requirements:
If the task completes within timeout window, control returns to the script (a prompt is displayed)
If the timeout is reached and the task is stil running, the task is killed and control returns to the script (a prompt is displayed)
Output from the task must be printed/displayed to stdout
Works over an SSH connection
Edited to use ping instead of notepad. I'm combining wait-process and "$?" into one statement with "$( )", because powershell "if" looks at the output, not the exit status.
start ping 'google.com -n 100'
if (-not $(wait-process ping 10; $?)) {
stop-process -name ping }
I think some of the requirement can be met by using a Stopwatch, you'll have to test the SSH connection.
$timeOut = New-TimeSpan -Seconds 5
$stopWatch = [System.Diagnostics.Stopwatch]::StartNew()
do {
try {
Test-Connection -ComputerName google.ca -Count 1 -ErrorAction Stop
$timeOut = New-TimeSpan -Seconds 0 #Stop the stopWatch, we got a response
}
catch { #Write-Host "No response" }
}
While ($stopWatch.elaspsed -lt $timeOut)
I am trying to perform a scheduled operation where I create a file share in Azure and copy some files in it. Before I begin doing so, I would like to cleanup the previous job if it did not cleanup on the prior run. To do so, I found the handy-dandy Remove-AzureStorageShare method. My problem is that after I call this method, it takes Azure sometimes up to 2 minutes to complete the task. I have a wait in PowerShell, but I'm unable to check Azure for the share without throwing an exception and then continue. So basically, I want the following operations to happen:
1] Check for Share in Azure, delete if it exists
2] Once Azure is done deleting, re-create it
3] Copy my files into the new Share
Here is what I have and it doesn't work:
Write-Host "STEP 6 : Removing existing Azure Share...";
# THIS NEXT LINE THROWS AN ERROR IF THE SHARE DOESN'T EXIST
If ((Get-AzureStorageShare -Name $azureShareName -Context $context) {
Remove-AzureStorageShare
-Context $context
-Name $azureShareName
-Force
-ErrorAction SilentlyContinue | Out-Null
}
$removed = $false;
While(!$removed) {
Try {
# THIS LINE SHOULD THROW AN EXCEPTION SINCE IT'S BEING DELETED
If ((Get-AzureStorageShare -Name $azureShareName -Context $context) -eq $null) {
$removed = $true;
}
}
Catch
{
# SINCE THE EXCEPTION IS THROWN, WE WILL SLEEP FOR A FEW...
Write-Host "STEP 6a : Waiting...still removing.";
Start-Sleep -s 10;
}
}
When I attempt to create the share again, I get the following error:
New-AzureStorageShare : The remote server returned an error: (409) Conflict. HTTP Status Code: 409 - HTTP Error Message: The specified share is being deleted. Try operation later.
Thank you for your question.
I think we can use this PowerShell to check for share in Azure:
Write-Host "STEP 6 : Removing existing Azure Share...";
# THIS NEXT LINE THROWS AN ERROR IF THE SHARE DOESN'T EXIST
If ((Get-AzureStorageShare -Name $azureShareName -Context $ctx)) {
Remove-AzureStorageShare `
-Context $ctx `
-Name $azureShareName `
-Force `
-ErrorAction SilentlyContinue | Out-Null
}
$removed = $false;
While(!$removed) {
Try {
# THIS LINE SHOULD THROW AN EXCEPTION SINCE IT'S BEING DELETED
If ((Get-AzureStorageShare -Name $azureShareName -Context $ctx) -eq $null) {
$removed = $true;
}
else {
Write-Host "STEP 6a : Waiting...still removing.";
Start-Sleep -s 5;
}
}
Catch
{
# SINCE THE EXCEPTION IS THROWN, WE WILL SLEEP FOR A FEW...
Write-Host "STEP 6b : Waiting...still removing.";
Start-Sleep -s 5;
}
}
If the Share existing the PowerShell will show: waiting…still removing, else the PowerShell will show error 404:
If you still have questions, welcome to post back here. Thanks.
I'm working on a large script where I run a Foreach loop, define variables in that loop and afterwards check if the $Server variable is pingable and if it is remotely accessible.
For this I use the following functions coming from the PowerShell help:
# Function to check if $Server is online
Function CanPing ($Server) {
$error.clear()
$tmp = Test-Connection $Server -ErrorAction SilentlyContinue
if ($?) {
Write-Host "Ping succeeded: $Server"; Return $true
}
else {
Write-Host "Ping failed: $Server."; Return $false
}
}
# Function to check if $Server is remotely accessible
Function CanRemote ($Server) {
$s = New-PSSession $Server -Authentication Credssp -Credential $Credentials -Name "Test" -ErrorAction SilentlyContinue
if ($s -is [System.Management.Automation.Runspaces.PSSession]) {
Enter-PSSession -Session $s
Exit-PSSession
Write-Host "Remote test succeeded: $Server."; Return $true
}
else {
Write-Host "Remote test failed: $Server."; Return $false
}
}
# Execute functions to check $Server
if ($Server -ne "UNC") {
if (CanPing $Server) {
if (-Not (CanRemote $Server)) {
Write-Host "Exit loop REMOTE" -ForegroundColor Yellow
continue
}
}
else {
Write-Host "Exit loop PING" -ForegroundColor Yellow
continue # 'continue' to the next object and don't execute the rest of the code, 'break' exits the foreach loop completely
}
}
Every time when I run this code, there is a process created on the remote server called wsmprovhost.exe. This process represents the PowerShell session, if the info I found on the web is correct. However, when doing Get-PSSession $Server there are no open sessions displayed in the PowerShell ISE, even though the processes are visible on the remote server and can only be killed with the Task Manager.
When I run this code often the limit of open sessions is reached because every time a new process wsmprovhost.exe is added to the $Server and the command errors out. I've tried to solve this by adding Exit-PSSessionto the code, but it doesn't close the session.
Any help or ideas are more than welcome.
The problem is that Enter-PSSession. Enter-PSSession can only be used interactively, you can't use it in a script. I'd suggest something more like this:
# Function to check if $Server is remotely accessible
Function CanRemote ($Server) {
Try {
$s = New-PSSession $Server -Authentication Credssp -Credential $Credentials -Name "Test" -ErrorAction Stop
Write-Host "Remote test succeeded: $Server."
$true
Remove-PSSession $s
}
Catch {
"Remote test failed: $Server."
$false
}
}
If I have understood correctly, Your remote ps-session are not getting closed.
To my understaning, Get-PSSession will show the session till your local session
is alive (I mean the session you created the remote ps-session) but once your local session
ends Get-PSSession will not show them cause they are no more live on your computer
rather on the remote system (or) they are no more in local session scope.
You can get the session using the command
Get-PSSession -ComputerName server_name
If you want to remove them you can do like
Get-PSSession -ComputerName server_name | Remove-PSSession
Even After executing the below command also, if you are not able to create session
Get-PSSession -ComputerName server_name | Remove-PSSession
Please, Restart the service Windows Remote Management (WS-Management) in the target machine.