Basic multiple if statement logic flow syntax in PowerShell - powershell

I can't seem to understand how to continue to another IF statement. What I'm trying to do is:
IF Action 1 succeed then do X AND go to Action 2, else log it and RETURN to the next loop
If Action 2 succeed then do X AND go to Action 3, else log it and RETURN to the next loop
I am having issues with "AND go to Action 2" after action 1. I tried various ways but obviously the script below does not work. It can do Action 1 the test-connection, and if it succeeds will export the log else it will log a failure and RETURN to the next loop. HOWEVER, I cannot make it do the next action if successful.
$hostname = Import-Csv C:\Users\jackie.cheng\Desktop\TestComputers.csv
$hostname | % {
if (Test-Connection $_.hostname -count 1)
{Write-Host "$($_.hostname) Test-Connection Succeeded"
$array += [pscustomobject]#{
Name = $currentobject.hostname
Status = "Test-Connection Success"}
}
else {Write-Host "$($_.hostname) Test-Connection Failed"
$array2 += [pscustomobject]#{
Name = $currentobject.hostname
Status = "Failed Test-Connection"}
} return
if (Test-Connection $_.hostname -count 1)
{Write-Host "Second action ran"}
else {Write-Host "Second action failed"} return
}

Within the script block of a Foreach-Object, which is the full name of the command with the alias %, you can in fact use return to skip any statements after the return, and continue on to the next loop.
In your case you simply need to move your return into your else blocks like this:
If (Success Condition){
Actions to do on success
}
else{
Actions to do on fail
return
}
If (Success Condition 2){
...
}
else{
...
return
}
As mentioned in the comments to your question, be sure to read up on how return works in PowerShell as it is somewhat unusual and behaves differently in different contexts.

I would do it this way, just output the objects for either case. Notice the indenting to help see the structure of the code. Return is usually not needed. -ea 0 means "-erroraction silentlycontinue".
Import-Csv TestComputers.csv |
% {
if (Test-Connection $_.hostname -count 1 -ea 0) {
[pscustomobject]#{
Name = $_.hostname
Status = $true}
}
else {
[pscustomobject]#{
Name = $_.hostname
Status = $false}
}
}
Name Status
---- ------
yahoo.com True
microsoft.com False

Related

How to count the process using PowerShell?

I would like to count how many times that I check the result.
This the part of the process looks like:
function GetValueResult
{
...Some Process...
$Result = $ReturnCode
}
GetValueResult
if ($Result -eq 0)
{
Write-Host "The process Pass"
Restart-Computer
}
elseif ($Result -eq 1)
{
Write-Host "Try again"
GetValueResult
#This part I need to know how many times that I call this function "GetValueResult"
}
Anyone can help me how to count how many times that I call the function "GetValueResult"?
If it already 3 times, than I need to do further action. Thanks for help.
You could add a simple loop inside your function and output an object with both the ResultCode and the number of tries:
function GetValueResult {
# initialize to error code
$ResultCode = -1
for ($numTries = 0; $numTries -lt 3; $numTries++) {
# ...Some Process...
# exit the loop if we have a correct $ResultCode
if ($ResultCode -eq 0) { break }
Write-Host "Try again"
}
# output an object with the resultcode and the number of tries
[PsCustomObject]#{
ResultCode = $ResultCode
Tries = $numTries + 1
}
}
$result = GetValueResult
if ($result.ResultCode -eq 0) {
Write-Host "The process Pass"
Restart-Computer
}
else {
Write-Host "Bad result after $($result.Tries) tries.."
}
You can use increment for this purpose.
$Result = 0
foreach (...){
$Result++
if ($Result -ge 3){
...your action...
}
}
Also, I can't find any loop in your script, so how would you count in this case? Please share more details about what are you trying to achieve with it, perhaps you need to redesign it from the beginning.

PowerShell script - distinguish between 3 command outcomes

Can anyone tell me why I'm always get the same output result?
$syslocal = Get-WmiObject -Class Win32_UserAccount -Filter "localaccount=true" |
where {$_.Disabled -eq $False}
if ($syslocal -eq "") {
Write-Host "Syslocal Enabled"
exit 0
} else {
Write-Host "No Syslocal"
exit 0
}
It just needs to see if a account syslocal exist and if it's enabled or not
with 3 outcomes:
write host: syslocal does not exist, exit 0
write host: syslocal exist, but is disabled, exit 0
write host: syslocal exist and is enabled, exit 1010
But all outcomes are the same.
If your Get-WmiObject | Where-Object statement finds a match the variable $syslocal will contain a list of WMI objects. If the statement doesn't find a match (i.e. no local accounts exist or all of them are disabled) the variable will be empty. Neither an empty value nor an array of objects equal the empty string, so your check will always evaluate to $false.
Also, for a 3-way check you need to actually check 3 ways.
Change your code to something like this and it should do what you expect.
$syslocal = Get-WmiObject -Class Win32_UserAccount -Filter 'localaccount=true'
if ($syslocal) {
if ($syslocal | Where-Object {-not $_.Disabled}) {
Write-Host 'Enabled local accounts exist.'
exit 1010
} else {
Write-Host 'Local accounts exist, but are disabled.'
}
} else {
Write-Host 'No local accounts.'
}
exit 0
this line is wrong:
if ($syslocal -eq "")
If it exist it's still not equal to ""
this should be:
if ($syslocal)
More details already explained on Ansgar Wiechers Answer...
Well there's this as well.
$syslocal = Get-WmiObject -Class win32_useraccount -filter "localaccount=true"
Foreach($account in $syslocal){
If ($syslocal) {
if($account.Disabled -eq $true) {
Write-Host "$($account.name) is currently Disabled"
} Else {
Write-Host "$($account.name) is currently Enabled"
}
} Else {
Write-host "Just no..."
}
}

Format-Table not displaying in function

I have a section of PowerShell code that reads a list of items from Azure, and formats them into a table for the user to choose from:
if ($SubscriptionArray.Count -eq 1) {
$SelectedSub = 1
}
# Get SubscriptionID if one isn't provided
while ($SelectedSub -gt $SubscriptionArray.Count -or $SelectedSub -lt 1) {
Write-host "Please select a subscription from the list below"
$SubscriptionArray | Select-Object "#", Id, Name | Format-Table
try {
$SelectedSub = Read-Host "Please enter a selection from 1 to $($SubscriptionArray.count)"
}
catch {
Write-Warning -Message 'Invalid option, please try again.'
}
}
When executed in the main area of the script, this outputs the expected result:
I want to use this logic multiple times, and therefore moved it into a method:
function Get-IndexNumberFromArray(
[Parameter(Mandatory = $True)]
[array]$selectArray,
[Parameter(Mandatory = $True)]
[string]$message
) {
[int]$SelectedIndex = 0
# use the current subscription if there is only one subscription available
if ($selectArray.Count -eq 1) {
$SelectedIndex = 1
}
# Get SubscriptionID if one isn't provided
while ($SelectedIndex -gt $selectArray.Count -or $SelectedIndex -lt 1) {
Write-Host "$message"
$selectArray | Select-Object "#", Id, Name | Format-Table
try {
$SelectedIndex = Read-Host "Please enter a selection from 1 to $($selectArray.count)"
}
catch {
Write-Warning -Message 'Invalid option, please try again.'
}
}
return $SelectedIndex
}
Everything in this method works great, except now my table is no longer outputting to the window. Instead, the user just get a prompt to pick a number from 1 to x with no context for what each number represents.
Why is the table working in the main area of the script, but not working in a function?
Format-Table actually doesn't print a table, it outputs objects that are then printed as the table. So if you're using a function, then the Format-Table output gets part of the return value of your function.
You can add Out-Host to the pipeline to force Format-Table's result to end up on the host, i.e. the console:
$selectArray | Select-Object "#", Id, Name | Format-Table | Out-Host

Including common code in header/footer with PowerShell

I have common functions and formats to most of my scripts. Each script brings up a window for me to paste workstations and it performs basic tasks like checking connectivity before proceeding. Generally, I copy and paste this code and modify the body. What I would like to do is include a header and footer, but I get "Missing closing '}' in statement block." errors. Example:
<# Begin Header #>
if($canceled) {
write-host "Operation canceled."
}
else {
if($computers.length -gt 0) {
[array]$computers = $computers.split("`n").trim()
# Loop through computers entered
foreach($pc in $computers) {
# Skip zero length lines for computers
if(($pc.length -eq $null) -OR ($pc.length -lt 1)) {
continue
}
else {
# Try to connect to the computer, otherwise error and continue
write-host "Connecting to: $pc$hr"
if(test-connection -computername $pc -count 1 -ea 0) {
<# End Header #>
Body of script
<# Begin Footer #>
}
else {
utc # Unable to contact
}
}
write-host "`n"
}
}
}
<# End Footer #>
Rather than copying/pasting each time, I would prefer to do this...
."c:\scripts\header.ps1"
-- code --
."c:\scripts\footer.ps1"
Is that even possible when the header ends with an open bracket? I do this in PHP but I can't figure out a work-around in PowerShell.
Your approach could be changed into storing a function in one file and your custom script that runs for-each server in another. You can store a scriptblock to a variable in PowerShell and pass that as a parameter to a function. You can use Invoke-Command -scriptblock $Variable to execute that code.
Write your function like this:
function runAgainstServerList {
param ( [ScriptBlock]$ScriptBlock)
if($canceled) {
write-host "Operation canceled."
}
else {
if($computers.length -gt 0) {
[array]$computers = $computers.split("`n").trim()
# Loop through computers entered
foreach($pc in $computers) {
# Skip zero length lines for computers
if(($pc.length -eq $null) -OR ($pc.length -lt 1)) {
continue
}
else {
# Try to connect to the computer, otherwise error and continue
write-host "Connecting to: $pc$hr"
if(test-connection -computername $pc -count 1 -ea 0) {
Invoke-Command -ScriptBlock $ScriptBlock
}
else {
utc # Unable to contact
}
}
write-host "`n"
}
}
}
}
Now save that off to your include file like 'myFunctions.ps1'
Then create your custom script that you want to run per server like this:
. myFunctions.ps1
[ScriptBlock]$ScriptBlockToPass = {
## Insert custom code here
}
runAgainstServerList $ScriptBlockToPass
To get you a step closer to what might be your end goal, You may want to append the -ComputerName "ComputerNameHere" argument to your invoke-command statement inside your included function. This would cause your script to be executed on the remote system instead of locally.

Powershell scripting for url custom monitoring

I am trying to build a custom script for URL monitoring. I am able to run the URL's from the file and enter the same in a logfile(named with time stamp).
Till here I have completed
Issue is when I compare the values from present(present timestamp) and previous logfile(previous timestamp).
This portion is not working fine. Please help me correct it.
Here is my code trying to compare value line by line from present logfile and previous logfile and run commands to generate output:
# New log is new logfile data
$Newlog = Get-Content $URLlogfile
$old_file = Dir C:\Scripts\logs | Sort CreationTime -Descending | Select Name -last 1
# Old log is Old logfile data
$oldlog = Get-Content $old_file -ErrorAction SilentlyContinue
Foreach($logdata in $Newlog) {
$url = ($logdata.Split(" "))[0]
$nodename = ($logdata.Split(" "))[1]
$statuscheck = ($logdata.Split(" "))[2]
$description = ($logdata.Split(" "))[3]
$statuscode = ($logdata.Split(" "))[4]
Foreach($log1data in $oldlog) {
$url1 = ($log1data.Split(" "))[0]
$nodename1 = ($log1data.Split(" "))[1]
$statuscheck1 = ($log1data.Split(" "))[2]
$description1 = ($log1data.Split(" "))[3]
$statuscode1 = ($log1data.Split(" "))[4]
While ($url = $url1) {
if ($statuscheck = $statuscheck1 ) {
write-output "output is same"
} elseif ($statuscheck = Fail) {
While ($statuscheck1 = Pass) {
write-output "$url is down at $nodename1- testing event sent"
}
} elseif ($statuscheck = Pass) {
While ($statuscheck1 = Fail) {
write-output "$url is up at $nodename1- testing event sent"
}
}
}
Break
}
}
#At end am clearing the old logs except present one
dir C:\Scripts\logs -recurse | where { ((get-date)-$_.creationTime).minutes -gt 3 } | remove-item -force
Per the comment from BenH, the following part of your code needs correcting as follows:
If ($url -eq $url1) {
if ($statuscheck -eq $statuscheck1 ) {
write-output "output is same"
} elseif ($statuscheck -eq 'Fail' -and $statuscheck1 -eq 'Pass') {
write-output "$url is down at $nodename1- testing event sent"
} elseif ($statuscheck -eq 'Pass' -and $statuscheck1 -eq 'Fail') {
write-output "$url is up at $nodename1- testing event sent"
}
}
Corrections:
In your comparison statements the = needs to be -eq. In PowerShell = always assigns a value.
In your comparison statements Pass and Fail need to be surrounded by single quotes so they are treated as strings (otherwise they are treated like function statements, for functions which don't exist).
I've replaced the While statements with If statements. I'm not sure what the intent of those was but I think they'd just get stuck in an infinite loop as the variable they test is never changed from within the loop.