I have a while($true) loop with a start-sleep -s 60 at the end of it. The purpose is to start an external PowerShell script as a different user which will run through a list of servers, check the event log for changes within the last minute and react accordingly.
Since my while loop (below) is using the -credential flag to run the script as someone else, I'm concerned about errors (e.g. account locked out, expired password, missing file, etc).
I tried an if ($error) statement, and changed the filename of the external script, but I was never alerted. I'm thinking it's because it never stops to re-check itself?
while($true) {
# Start the scan
Start-Process powershell -Credential $credentials -ArgumentList '-noprofile -command & c:\batch\02-Scan.ps1'
# Sleep 60 seconds
start-sleep -s 60
}
I suppose I could change my scheduled task to run every minute, but so far this loop seems to have been working great. Just want to introduce error checking while the loop is active.
Have you tried try/catch blocks? Wrong credentials is a terminating error, so the rest of the code in the try block won't run after a credentials-error. When you catch it, you can do whatever you want.. Ex.
try {
Start-Process powershell -Credential $credentials -ArgumentList '-noprofile -command & c:\batch\02-Scan.ps1'
} catch {
#Catches terminating exceptions and display it's message
Write-Error $_.message
}
If you want to catch all errors, add -ErrorAction Stopto the Start-Processline. As said, credentials should be a terminating error, which make the erroraction parameter unnecessary
EDIT Why would you use Start-Process to run a script in the first place? I switched it to Invoke-Command which runs a powershell script remotely. When the scriptfile is missing, you will recieve a NON-terminating error. Because it's a non-terminating error, we need to use the -ErrorAction Stop parameter. To catch the missing-file error and every other error(like credentials), use something like this:
try { Invoke-Command -ScriptBlock { & c:\batch\02-Scan.ps1 } -ErrorAction Stop
} catch {
if ($_.Exception.GetType().Name -eq "CommandNotFoundException") {
Write-Error "File is missing"
} else {
Write-Error "Something went wrong. Errormessage: $_"
#or throw it directly:
#throw $_
}
}
Maybe?
while($true) {
# Start the scan
try{
Start-Process powershell -Credential $credentials -ArgumentList '-noprofile -command & c:\batch\02-Scan.ps1' -ErrorAction Stop
}
catch {
send-alert
break
}
# Sleep 60 seconds
start-sleep -s 60
}
Related
I have a script that stops a service then renames a folder on the local computer, i would like to be able to make a run remotely by asking the command executer to enter the machine name of where it would like to run the script then execute.
try {
Stop-service wuauserv -force
} catch {
Write-error "Unable to stop the service WUAUSERV"
start-sleep -seconds 5
exit
}
try {
Rename-Item "C:\Windows\SoftwareDistribution" "C:\Windows\SoftwareDistribution.old" -force
} catch {
Write-error "Unable to rename the folder :("
start-service wuauserv
start-sleep -seconds 5
exit
}
start-service wuauserv
I would probably have the script take a parameter by adding this at the top
Param(
[Parameter(Mandatory)][string]$ComputerName
)
The Mandatory value will prompt the user to enter a value if it was not supplied.
Alternatively if you really want to manually ask the user you can use this:
$ComputerName = Read-Host "ComputerName"
This will also prompt the user to enter a variable.
Then use Invoke-Command to run your logic on the remote computer
Invoke-Command -ComputerName $ComputerName -ScriptBlock {
# Your code here..
}
Edit:
After your update, I notice you terminate the script by calling exit. I would advise you to use return instead. If someone runs this script from the commandline the script will kill the terminal, which is quite annoying.
Edit2: Here is a complete working example, with my recommendations:
Param(
[Parameter(Mandatory)][string]$ComputerName
)
Invoke-Command -ComputerName $ComputerName -ScriptBlock {
try {
Stop-Service wuauserv -Force
} catch {
Write-error "Unable to stop the service WUAUSERV"
Start-Sleep -Seconds 5
return
}
try {
Rename-Item "C:\Windows\SoftwareDistribution" "C:\Windows\SoftwareDistribution.old" -Force
} catch {
Write-error "Unable to rename the folder :("
Start-Service wuauserv
Start-Sleep -Seconds 5
return
}
Start-Service wuauserv
}
I am not quite sure how to explain my problem, but I have a function that installs Office, imagine the person that runs this script does not have internet connection or does not have enough space on her hard drive. I have the XML file set to hide the setup interface so the user can't see the installation process. Just to be clear all my code works fine, just want add this feature so that if something goes wrong while the user runs the script I know where the error was.
This is my function:
Function Install-Office365OfficeProducts{
Write-Host ""
Start-Sleep -Seconds 5
Write-Host "Installing Office 365 ProPlus..."
# Installing Office 365 ProPlus
Install-Office365Product -path "$PSScriptRoot\setup.exe" -xmlPath "$PSScriptRoot\InstallO365.xml"
This is what I have tried:
if (Install-Office365OfficeProducts -eq 0) {
Write-Host "FAILED"}
I am very confused, I thought that a function that runs with no error returns 1 and when it runs with errors returns 0.
Also have tried to put the code like this:
try {
Install-Office365Product -path "$PSScriptRoot\setup.exe" -xmlPath "$PSScriptRoot\InstallO365.xml"
} catch {
Write-Host "Failed!"
}
EDIT:
Basically i want to be shown an error if the Office setup is not finished...
#Thomas
Function Install-Office365Product{
Param (
[string]$path,
[string]$xmlPath
)
$arguments = "/configure `"$xmlPath`""
try{
Start-Process -FilePath "$path" -ArgumentList "$arguments" -Wait -NoNewWindow -ErrorAction Stop
}catch{
Write-Host "It was not possible to install the product!"
}
}
Your try/catch-block inside Install-Office365OfficeProducts is useless, because Install-Office365Product will not throw anything, except you pass wrong arguments. The try/catch-block inside Install-Office365Product will most likely also not catch anything. But you can of course evaluate the return code of your installer called with Start-Process:
function Install-Office365Product {
Param (
[string]$path,
[string]$xmlPath
)
$arguments = "/configure `"$xmlPath`""
$process = Start-Process -FilePath "$path" -ArgumentList "$arguments" -Wait -PassThru -NoNewWindow
if ($process.ExitCode -eq 0) {
Write-Host "Installation successful"
} else {
Write-Host "Installation failed"
}
}
Instead of writing to stdout, you can of course also throw an exception and handle it later in a higher function.
When I try to kick off a Delta Sync through a pssession a error is thrown. If I run the commands one line at a time it runs perfectly fine.
Enter-PSSession server_name
Get-Module ADSync | Import-Module
Start-ADSyncSyncCycle -PolicyType Delta
Exit-PSSession
I expect it to start the Sync but just get the error:
'Microsoft.DirectoryServices.MetadirectoryServices.UI.PropertySheetBase.UIUtils'
threw an exception.
I usually perform a Start-ADSyncSyncCycle through a helper function that detects if the ADSyncScheduler is suspended and also detects if there maybe already a sync cycle in progress. Without watching for these things you may receive errors.
function Start-SyncCycle {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false, Position = 0)]
[ValidateSet('Delta','Initial')]
[string]$PolicyType = 'Delta'
)
# test if the ADSyncScheduler is not suspended
$scheduler = Get-ADSyncScheduler
if ($scheduler.SchedulerSuspended) {
# SchedulerSuspended is set during an upgrade to temporarily block the scheduler from running.
# When this property is $true, running Start-ADSyncSyncCycle wil result in error:
# System.InvalidOperationException: Scheduler is already suspended via global parameters.
Set-ADSyncScheduler -SchedulerSuspended $false
}
# test if a scheduled synchronization is already running
if ($scheduler.SyncCycleInProgress) { # or use: if (Get-ADSyncConnectorRunStatus) { ... }
Write-Warning "A sync is already in progress. Please try again later."
}
else {
Write-Host "Initializing Azure AD $PolicyType Sync..." -ForegroundColor Yellow
try {
Start-ADSyncSyncCycle -PolicyType $PolicyType -ErrorAction Stop | Out-Null
Write-Host "Waiting for Sync to start.."
# give the Sync Connector some time to start up (10 seconds should be enough)
Start-Sleep -Seconds 10
Write-Host "Waiting for Sync to finish.."
While(Get-ADSyncConnectorRunStatus) {
Write-Host "." -NoNewline
Start-Sleep -Seconds 5
}
Write-Host
Write-Host "Azure AD $PolicyType Sync has finished." -ForegroundColor Green
}
catch {
Write-Error $_.Exception.Message
}
}
}
You use the function like this:
$cred = Get-Credential -UserName "your.username#yourdomain.com" -Message "Please enter credentials for yourdomain.com"
Invoke-Command -ComputerName $AzureConnectServer -ScriptBlock ${function:Start-SyncCycle} -Credential $cred
Hope that helps
The solution that ended up working out for me was to invoke the command rather then enter a pssession. I am not sure why this works, but it does.
$cred = Get-Credential
Invoke-Command -ComputerName server_name -Credential $cred -ScriptBlock{
Import-Module -Name "C:\Program Files\Microsoft Azure AD Sync\Bin\ADSync"
Start-ADSyncSyncCycle -PolicyType Delta
}
There's a strange issue in my script.
I'm invoking a command on some servers. If I can't connect to the server (because it's offline or something) I still want to log that (Server is offline > log.txt)
Apparently, when an error occurs in Try block, the Catch block is not executed.
To test this, I wrote a value to the ComputerName parameter that doesn't exist.
Here's my code:
Try {
Invoke-Command -ComputerName hui -ScriptBlock $sb
}
Catch {
Write-Host "Hello"
}
But the Script never puts out "Hello"
The exception I get is a PSRemotingTransportException. I also tried to specify the exception, also didn't work
When I set a breakpoint at the Invoke-Command line, it also ignores the catch block. Why?
It's not a terminating error, so it will never be caught by try/catch. Add -ErrorAction Stop:
Try {
Invoke-Command -ComputerName hui -ScriptBlock $sb -ErrorAction Stop
}
Catch {
Write-Host "Hello"
}
I'm running this simple script of MSIEXEC uninstall:
$script = {
invoke-expression "msiexec /qn /x '{C5CF41A6-A65A-4BB3-8C6C-87FF4A730EFD}' "
$logcheck = ""
while($true)
{
if($logcheck -match "Removal success or error status: 0")
{
return
}
else
{
start-sleep -Seconds 1
[string]$logcheck = get-eventlog -logname application -newest 1 | foreach-object {$_.message}
}
}
}
Invoke-Command -computername Comp-name -Credential Domain\user -scriptblock $script
Write-Host "Done"
Everything seems to work great, the MSI was being uninstalled successfully but the powershell process stays open and doesn't stop.
Any idea how can I stop it?
It doesn't stop because of the while($true)
That logcheck is not really full proof, it gets the newest log item and then checks for status "Removal success or error status: 0". If any other application adds an entry to the log at the same time, this loop will run forever.
Better to get all log items since a certain date, e.g. start datetime of msiexec and look for the success message. (and not use the loop)