Powershell: -or in a while loop - powershell

What is wrong with my formatting. the code runs when only one condition is in there. When I add the -or with the second condition to check it appears to not check either.
while(($TempLog.id -ne "18") -or ($TempLog.id -ne "32"))
{
$TempLog = Get-WinEvent -computername F9P8NW2-5300 -LogName 'Microsoft-Windows-RemoteAssistance/Operational'-MaxEvents 1
}
enter code here

Related

Powershell bitlocker check

Hi im having diffeculties to get my script working:
It keeps failing on the first write output, even when the powershell version is higher then 4. It only works when I remove And $winver -eq $os1 -or $os2 -or $os3.
Otherwise it keeps telling me my powershell version needs to be upgraded. Im on V5 currently and $PSVersionTable.PSVersion.Major does says it 5 indeed.
What am i doing wrong?
$winver = (Get-WmiObject -class Win32_OperatingSystem).Caption
$powershellversion = $PSVersionTable.PSVersion.Major
$os1 = "Microsoft Windows 7 Professional"
$os2 = "Microsoft Windows 10 Pro"
$os3 = "Microsoft Windows 10 Enterprise"
if($winver -ne ($os1, $os2, $os3) -contains $winver){
Write-Host "Bitlocker not supported on $winver"
Exit 0
}
if($powershellversion -lt 4){
Write-Host "Upgrade Powershell Version"
Exit 1010
}
else
{
$bitlockerkey = (Get-BitLockerVolume -MountPoint C).KeyProtector.RecoveryPassword
$pcsystemtype = (Get-WmiObject -Class Win32_ComputerSystem).PCSystemType
if ($pcsystemtype -eq "2"){
$setsystemtype = "Laptop"
}
else {
$setsystemtype = "Desktop"
}
if ($setsystemtype -eq "laptop" -And $bitlockerkey -eq $null -and ($os1, $os2, $os3) -contains $winver){
Write-Host "$setsystemtype without bitlocker"
Exit 1010
}
if ($setsystemtype -eq "desktop" -And $bitlockerkey -eq $null -and ($os1, $os2, $os3) -contains $winver){
Write-Host "$setsystemtype without bitlocker"
Exit 0
}
if ($winver -eq ($os1, $os2, $os3) -contains $winver){
Write-Host "$bitlockerkey"
Exit 0
}
}
Let's see what this actually does:
if ($powershellversion -lt 4 -And $winver -eq $os1 -or $os2 -or $os3) { ... }
If your powershell version is less than 4, and Win version is equal
to os1, then proceed
If os2 has a value, then proceed
If os3 has a value, then proceed
The topic here is Operator Precedence, specifically, what happens first when evaluating a line of code, what happens second, third, and so on. Just like in algebra math, adding parens around part of the formula changes the order in whcih you read it.
So, you can mess around with parens to get your logic to work:
if($powershellversion -lt 4 -and ( ($winver -eq $os1) -or ($winver -eq $os2) -or ($winver -eq $os3) ))
In other words
evaluate if PS version is < 4 ($powershellversion -lt 4), -and
evaluate if winver is either os1, os2 or os3: ( ($winver -eq $os1) -or ($winver -eq $os2) -or ($winver -eq $os3) ).
Or, you can rearrange your logic a bit by putting your os variables into an array, and seeing if $winver is in there:
if($powershellversion -lt 4 -and $winver -in ($os1, $os2, $os3)) { ... }
Edit: Or
if($powershellversion -lt 4 -and ($os1, $os2, $os3) -contains $winver) { ... }
for backwards compatibility with v2.0.

Script not assigning WMI objects to array correctly

I am trying to create a script that detects domain user profiles that are up to 15 days old and removes them. This will be bundled in a job that will log out any idle sessions before it runs.
I am aware that this would normally be done via GPO, however it is not fit for purpose for this particular business area for various reasons.
Here is my code:
#Assign how old the user profile must be for deletion
[int]$NoOfDays = 15
#Get WMI object where it is a domain account and over 15 days old
$Objects = #(Get-WmiObject -Class Win32_UserProfile | Where {
(!$_.Special) -and
$_.ConvertToDateTime($_.LastUseTime) -lt (Get-Date).AddDays(-15)
})
if ($Objects -eq $null) {
#If no users returned, write to host.
Write-Host "no profiles found for deletion"
} else {
#If users are found, do the following
foreach ($Object in $Objects) {
Write-Host "'$($object.LocalPath)' has been identified for deletion"
}
foreach ($Object in $Objects) {
try {
Write-Host "Attempting to delete profile '$($Object.LocalPath)'"
Remove-WmiObject
Write-Host "Deleted profile '$($Object.LocalPath)' successfully."
} catch {
Write-Host "Unable to delete profile '$($Object.LocalPath)'" -ErrorAction Continue
}
}
}
There is no output, it just returns to command line with no error straight away.
I have attempted commenting out the bit that removes the profile, ending the else after the first for each statement to see what is identified for deletion however it has not worked.
$Objects = #(...) ensures that $Objects is an array, even if the command doesn't return a result. An array (even an empty array) never equals $null, so your first condition is never triggered.
Change
if ($Objects -eq $null) {
to
if ($Objects.Count -gt 0) {
and the code should do what you expect.
Alternatively to Ansgar's solution you could avoid assigning the $Objects variable to an array (can't see why this is required).
$Objects = Get-WmiObject -Class Win32_UserProfile | Where {
(!$_.Special) -and
$_.ConvertToDateTime($_.LastUseTime) -lt (Get-Date).AddDays(-15)
}
-eq $null check can then be used. You may also find [string]::IsNullOrEmpty($Object) useful as this will detect an empty array.
You mention there is no output... that's suspect as you should get the Write-Hosts printing something. On my machine I got an error with $_.ConvertToDateTime... I recommend running your code a line at a tie in the console to make sure it's doing what you expect.
Ok, so it turns out that it was a combination of issues. The main one is the PS version it runs on, does not work on 4. The next stage is to try and force PowerShell to run it as version 3. As previously stated it does not need to be handled within an array. Here is my final script (does not like returns after each and condition so left the formatting as is):
if ($PSVersionTable.PSVersion.Major -eq 4)
{
Write-host "this script will terminate as machine is running PowerShell version 4"
exit 1
}
Else
{
Start-Sleep -s 5
Set-Location -Path C:\Users
#Assign how old the user profile must be for deletion
[DateTime]$AdjustedDate = (Get-Date).AddDays(-15)
[DateTime]$CompareDate = Get-Date $AdjustedDate -Format MM-dd-yyyy
$Hostname = $env:computername
#$testdate = Get-WmiObject -Class Win32_UserProfile | select {$_.ConvertToDateTime($_.lastusetime).ToShortDateString()}
#write-host $testdate
#Get WMI object where it is a domain account and over 15 days old
$Objects = Get-WmiObject -Class Win32_UserProfile | Where {(!$_.Special) -and ($_.ConvertToDateTime($_.lastusetime) -lt (Get-Date).AddDays(-15)) -and $_.LocalPath -notlike "*.NET*" -and $_.LocalPath -notlike "*SQL*" -and $_.LocalPath -notlike "*Admin*" -and $_.LocalPath -notlike "*ADMIN*"}
#If no users returned, write to host.
If($Objects.Count -eq 0)
{
Write-host "no profiles found for deletion"
}
#If users are found, do the following
Else
{
Foreach($Object in $Objects)
{
Write-host "'$($object.LocalPath)' has been identified for deletion"
Write-host " "
}
Foreach($Object in $Objects)
{
Try{
Write-Host "Attempting to delete profile '$($Object.LocalPath)'"
$UserSID = (Get-WmiObject Win32_UserProfile | Where {$_.LocalPath -like '$($object.LocalPath)'}).SID
Remove-WmiObject -InputObject $Object
Write-Host "Deleted profile '$($Object.LocalPath)' successfully."
}
Catch{
Write-Host "Unable to delete profile '$($Object.LocalPath)' due to the following error"
Write-Host "$error" -ErrorAction Continue
}
}
}
}

Why is line continuation needed inside a scriptblock?

Why does PowerShell need a line continuation backtick after the first condition of the Where-Object scriptblock? It should be clear that the scriptblock has not yet ended as there has been no closing curly brace }.
Why does this work:
Get-Process | Where-Object {
($_.Responding -ne $true) `
-or ($_.ProcessName -like 'W*')
}
and this doesn't:
Get-Process | Where-Object {
($_.Responding -ne $true)
-or ($_.ProcessName -like 'W*')
}
This also fails.
Get-Process | Where-Object {
(
($_.Responding -ne $true)
-or ($_.ProcessName -like 'W*')
)
}
Your first example works, because you tell PowerShell that the statement is continued in the next line (by escaping the linebreak).
The other two examples fail, because the expression on the first line is a complete expression in and by itself, whereas the expression on the second line is not. PowerShell first evaluates ($_.Responding -ne $true), then tries to evaluate -or ($_.ProcessName -like 'W*'), which is an invalid statement and thus fails.
To avoid this you need to make it clear to the interpreter that the statement on the first line is continued in the next line. That can be done either by escaping the linebreak like you do in your first example:
Get-Process | Where-Object {
($_.Responding -ne $true) `
-or ($_.ProcessName -like 'W*')
}
or by constructing your statement in a way that allows the interpreter to detect that there is more to come, e.g. by putting the operator at the end of the first line instead of at the beginning of the second line:
Get-Process | Where-Object {
($_.Responding -ne $true) -or
($_.ProcessName -like 'W*')
}
The curly braces can't be used to detect the end of the statement, because it's valid to put multiple statements in a Where-Object scriptblock.

powershell restart service if failed

i have the Problem that an Service is crashing whitout stopping.
This means the status is shown as running but...
However - i wrote a small (absolute Beginner(!)-)Powershell-Script to check if the app is crashed, but how do i have to continue?
If the Script finds an entry in the Eventlog it shoud stop and start the Service..
Clear-Host
$timetocheck = [DateTime]::Now.AddMinutes(-10)
$eventid = "10016"
$log = "System"
$app = "SID"
$check = "Get-WinEvent -LogName $log | Where-Object {($_.TimeCreated -ge $timetocheck) -and ($_.id -eq $eventid) -and ($_.Message -Like *$app*)}"
edit
just to clarify -
if this snippet finds nothing in the eventlog nothing should happen.
if this snippet finds at least 1 error in the eventlog the service should be stopped and restarted.
with other words - if process crashed restart else do nothing
thx
Well - now i can answer my own question.. ;)
This works:
Clear-Host
$timetocheck = [DateTime]::Now.AddMinutes(-30)
$eventid = "10016"
$log = "System"
$app = "SID"
$checking = Get-WinEvent -FilterHashtable #{Logname="$log";ID="$eventid" ;StartTime="$timetocheck"}|`
Where-Object {$_.Message -like "*$app*"}
if ($checking -like "*") {ReStart-Service -Name DistributedCOM -Force}
The Trick is the $checking -like "*". I´m not satisfied completely because this "only" checks if the Get-Winevent replys at least one sign. I would prefer to search for a string i know....
When the string to check is shorter its working with a defined string....
However - its working. And thats important. And maybe someone else needs this to.
thx to all
edit
and the first improvment....
the command Get-WinEvent -FilterHashtable #{Logname="$log";ID="$eventid" ;StartTime="$timetocheck"}| Where-Object {$_.Message -like "$app"}
takes 0,7 seconds
the command Get-WinEvent $log | Where-Object{($.TimeCreated -ge $timetocheck) -and ($.id -eq $eventid) -and ($_.Message -Like "$app")} takes 4,2 seconds
so i changed it
Try it with the following code
Clear-Host
$timetocheck = [DateTime]::Now.AddMinutes(-10)
$eventid = "10016"
$log = "System"
$app = "SID"
$check = Get-WinEvent -LogName $log | Where-Object {($_.TimeCreated -ge $timetocheck) -and ($_.id -eq $eventid) -and ($_.Message -Like "*$app*")}
if ($check -ne $null)
{
Restart-Service -Name YourService -Force
}

Filter on WMI. If xp then proceed otherwise fail

The if condition works until I try to add the last portion of the line in there. I want it to look at the value of the operating system and if it is equal to 5.* then proceed with the statement. Otherwise fail.
$OS = Get-WmiObject Win32_OperatingSystem -computer $Machine
$OSVER = $OS.VERSION
if (($IEVersion -like '8*') -and (($Machine -like '[NCI]*') -or ($Machine -like '[RXPC]*') -or ($Machine -like '[RXCO]*') -or ($Machine -like '[MW]*') -or ($Machine -like '[SMPC]*') -or ($Machine -like '[MTPC]*')) -and ($OSVER -like '5.*'))
{
Write-Host "Do something here"
}
else
{
Write-Host "Do nothing on the machine"
}
All I did was put the WMI call outside of the IF statement. All works as expected. I have updated the code above.