Where clause not filtering properly - powershell

Using the script below I am trying to filter out applications that don't have a $requirement like Windows 10. When I run this I am still getting returned results with application requirements containing windows 10.
| Where { $_ -notlike 'All_x64_Windows_10_and_higher_Clients' };
Any idea what I am doing wrong here? Possible issue with the line above?
$warningpreference = "SilentlyContinue"
Get-Content C:\temp\Applications.txt | foreach-object {
$app = Get-CMApplication -Name "$_";
[XML]$appXML =$app.SDMPackageXML;
$Requirement = $appXML.AppMgmtDigest.DeploymentType.Requirements.Rule.OperatingSystemExpression.Operands.RuleExpression.RuleID | Where { $_ -notlike 'All_x64_Windows_10_and_higher_Clients' };
If ($Requirement -ne $null -or $Requirement.length -gt 0) {
Write-output "Application Name: $_ | Requirement: $Requirement "
}
}

The -Like Operator is used for WildCard searches in PowerShell. So you need an * somewhere in your filter.
Try this:
| Where { $_ -notlike "*All_x64_Windows_10_and_higher_Clients*" };

Related

Nesting ForEach within If statement (PowerShell)

I am trying to loop through a list of Windows services and if any of them match a certain criteria, I would like to return exit code 1.
If there is no match, I would like to return exit code 0.
I am struggling to put this within an If statement, I think I'm putting the code in the wrong place!
Could anyone lend me a hand? Script below.
Thanks in advance.
Adrian
try
{
#Pull list of services from registry
$svclist = Get-ChildItem HKLM:\SYSTEM\CurrentControlSet\services | ForEach-Object {Get-ItemProperty $_.PsPath}
#Ignore anything after .exe, filter for vulnerable services
ForEach ($svc in $svclist) {
$svcpath = $svc.ImagePath -split ".exe"
if(($svcpath[0] -like "* *") -and ($svcpath[0] -notlike '"*') -and ($svcpath[0] -notlike "\*")) {
$svc | fl -Property DisplayName,ImagePath,PsPath
}
}
if (($svc -ne $null)){
Write-Host "Match"
Return $svc.count
exit 1
}
else{
Write-Host "No_Match"
exit 0
}
}
catch{
$errMsg = $_.Exception.Message
Write-Error $errMsg
exit 1
}
You can either return early or use a variable with a single [bool] value to keep track of whether anything was matched:
return early
foreach($svc in $svcList)
{
$svcpath = $svc.ImagePath -split ".exe"
if(($svcpath[0] -like "* *") -and ($svcpath[0] -notlike '"*') -and ($svcpath[0] -notlike "\*")) {
return 1
}
}
# if we've reached this point then no matches occurred
return 0
Using a [bool]
$matchFound = $false
foreach($svc in $svcList)
{
$svcpath = $svc.ImagePath -split ".exe"
if(($svcpath[0] -like "* *") -and ($svcpath[0] -notlike '"*') -and ($svcpath[0] -notlike "\*")) {
$matchFound = $true
}
}
return [int]$matchFound # $false = 0, $true = 1
I always try to use the most powershell-correct methods to achieve what I want. Especially when going through data I prefer to try filtering with the x-object cmdlets.
In your case my suggestion would be to simply loop over the original list with the Where-Object command, this allows you to retrieve a list of items that conform to your search, kind of like an SQL query:
$resultList = $svclist | Where-Object {
($_.ImagePath -like "* *") -and ($_.ImagePath -notlike '"*') -and ($_.ImagePath -notlike "\*")
}
In this case I skipped over the -split ".exe" part, as I didn't quite understand it's purpose, but you could also put that in your filter using regular expressions with the -match operator instead of the -like and -notlike values you make one regex match
Then you can check if that list is populated or not:
if ($resultList) {
return 1
}
else {
return 0
}
It is also considered best practice to only use the aliases for commands (e.g. fl should be Format-List). This will increase readability for future maintenance, of course if it's a one-time script is would be more appropriate. I just try to avoid it as much as I can these days.

how to stop multiple services running exclude services which are specified in file

I am trying to stop multiple services running with specific name.
I would like to have a PowerShell script i can execute and let stop all services exclude services I specify in a text file.
help please.
$ServiceRunName = Get-Service | where {($_.Name -like "ser_*") -and ($_.Status -eq "Running")} | Select-Object -Property Name
$fileServices = Get-Content ..\run.txt
If ($fileServices -ne $null) {`enter code here`
foreach ($line in $fileServices ) {
$service = $line
"$service"
"$name"
if($service -eq $ServiceRunName ){
" not kill "
}else {
"kill"
}
}
}
Below code defines an array, including the names of the services NOT to stop, instead of reading a file:
# Alternative to array: $servicesNotToStop = Get-Content ..\run.txt
$servicesNotToStop = #("WlanSvc","vpnagent")
Get-Service | ? {$_.Status -eq "Running"} | ? { $servicesNotToStop -notcontains $_.Name} | % { Stop-Service $_ -WhatIf}
Get-Service | ? {$_.Status -eq "Running"} ... Get all services in state running and send them to the pipeline.
? { $servicesNotToStop -notcontains $_.Name} ... check if the actual object ($_) in the pipieline (type=ServiceController) is NOT included in the array (the name property includes the service name).
% { Stop-Service $_ -WhatIf} ... perform stop service foreach object that was fowarded by beforehand ?-filter. The -WhatIf-switch of Stop-Service prevents stopping the serivce, BUT it prints what service would be stopped. A lot of PowerShell cmdlets offer WhatIf-switches to test the command in a "dry"-run.
From msdn:
-WhatIf
Shows what would happen if the cmdlet runs. The cmdlet is not run.
UPDATE 1
Read services that shouldn't be stopped from file:
$servicesToStop = Get-Content .\dontStop.txt
Get-Service | ? { $_.Status -eq "Running" } | ? { $servicesToStop -notcontains $_.Name } | % { Stop-Service $_ -WhatIf }
Format of dontStop.txt:
service1
service2
UPDATE 2
More or less the code of you question:
$ServiceRunName = Get-Service | where-object { $_.Status -eq "Running" } | Select-Object -ExpandProperty Name
$fileServices = Get-Content "..\run.txt"
If ($null -ne $fileServices) {
foreach ($service in $ServiceRunName) {
foreach ($serviceNotToStop in $fileServices ) {
if ($service -ne $serviceNotToStop ) {
Stop-Service $service -WhatIf
break
}
}
}
}
Hope that helps

PowerShell Hash table updates apply to all entries

I am trying to update an hash table entry (Content, which is an array) where I find values - but when I try to set the value for $_ it applies to all entries in the entire hash ($hash).
$ContentArray = #($null, $null)
$Comparison | Add-Member -MemberType NoteProperty -Name "Content" -value $ContentArray
If($GetAdvancedData -eq "true"){
$Hash| ForEach-Object{
If ($_.VarianceType -ne "Missing")
{
$_.Item
$id = $_.id[0]
$elem = $_.elementName
Write-debug "Checking $elem"
Write-debug "Checking for version $id"
try{
$content = Get-VersionContent -FilteredVersionID $id
Write-debug "Content found"
Write-debug "$content"
# Various tests to try and set the value:
#ForEach ($Key in $Hash.GetEnumerator() | Where-Object {$_.id -eq $id}){$Key.Content[0] = $content}
#$Hash.Content[0] = "$content" | Where-Object {$Hash.id[0] -eq $id}
#$Hash.DiffType = "Content"| Where-Object {$_.id[0] -eq $id}
# $_.content.SetValue("$content","0")
$_.Content[0] = $content
# Reset $content to Null
$content = $null
}
catch
{
Write-debug "No content found"
}
}
}
I have tried setting it via a where clause based on another key value, using SetValue, and simply doing an = statement, but in each case it sets the entire hash tables content to $content - I feel as if I must be missing something obvious, but I can't see why (if I use the PowerShell ISE and debug $_ returns only the single record from the ForEach loop)
A silly mistake-
$Hash.GetEnumerator() | Where-Object {$_.id[0] -eq $id} | foreach{$_.Content = $content,$null}
did the trick for me!

Like in Where-Object doesn't work in PowerShell 2

I have a piece of script to do some stuff on VMs. Here it is:
$powered = Get-VM VM-TEST4-* | Where-Object { $_.PowerState -eq 'PoweredOn'
ForEach-Object -InputObject $powered { Shutdown-VMGuest -VM $_ }
# taking snapshots etc etc
# BELOW FAILS
# start up machines in order
Where-Object -Like -Property Name -InputObject $powered -Value "VM-TEST4-DB*" | ForEach-Object { if ($_ -ne $null) { Start-VM -VM $_ } }
sleep -Seconds 180
Where-Object -Like -Property Name -InputObject $powered -Value "VM-TEST4-AUX*" | ForEach-Object { if ($_ -ne $null) { Start-VM -VM $_ } }
sleep -Seconds 180
Where-Object -Like -Property Name -InputObject $powered -Value "VM-TEST4-WEB*" | ForEach-Object { if ($_ -ne $null) { Start-VM -VM $_ } }
sleep -Seconds 180
My problem with this code is that nothing gets started and I only wait. Now the idea behind it was to filter out correct server type so that DB starts earlier than AUX and WEB while $_ -ne $null check protects me if Where-Object returns no results (we have an enviroment with no AUX servers).
Any idea how to make it work properly?
The simplified where-object syntax (including the -like switch parameter) was introduced in 3.0. The -like operator works fine in the standard where-object syntax. You want something like this:
Where-Object {$_.Name -like "VM-TEST4-WEB*"}
EDIT:
The -Like parameter for where-object (which looks like an operator in the simplified syntax) was added in 3.0. The -Like operator which does string comparisons has been in PowerShell since 1.0

Use -notlike to filter out multiple strings in PowerShell

I'm trying to read the event log for a security audit for all users except two, but is it possible to do that with the -notlike operator?
It's something like that:
Get-EventLog -LogName Security | where {$_.UserName -notlike #("*user1","*user2")}
I have it working for a single user, like:
Get-EventLog -LogName Security | where {$_.UserName -notlike "*user1"}
V2 at least contains the -username parameter that takes a string[], and supports globbing.
V1 you want to expand your test like so:
Get-EventLog Security | ?{$_.UserName -notlike "user1" -and $_.UserName -notlike "*user2"}
Or you could use "-notcontains" on the inline array but this would only work if you can do exact matching on the usernames.
... | ?{#("user1","user2") -notcontains $_.username}
I think Peter has the right idea. I would use a regular expression for this along with the -notmatch operator.
Get-EventLog Security | ?{$_.Username -notmatch '^user1$|^.*user$'}
In order to support "matches any of ..." scenarios, I created a function that is pretty easy to read. My version has a lot more to it because its a PowerShell 2.0 cmdlet but the version I'm pasting below should work in 1.0 and has no frills.
You call it like so:
Get-Process | Where-Match Company -Like '*VMWare*','*Microsoft*'
Get-Process | Where-Match Company -Regex '^Microsoft.*'
filter Where-Match($Selector,[String[]]$Like,[String[]]$Regex) {
if ($Selector -is [String]) { $Value = $_.$Selector }
elseif ($Selector -is [ScriptBlock]) { $Value = &$Selector }
else { throw 'Selector must be a ScriptBlock or property name' }
if ($Like.Length) {
foreach ($Pattern in $Like) {
if ($Value -like $Pattern) { return $_ }
}
}
if ($Regex.Length) {
foreach ($Pattern in $Regex) {
if ($Value -match $Pattern) { return $_ }
}
}
}
filter Where-NotMatch($Selector,[String[]]$Like,[String[]]$Regex) {
if ($Selector -is [String]) { $Value = $_.$Selector }
elseif ($Selector -is [ScriptBlock]) { $Value = &$Selector }
else { throw 'Selector must be a ScriptBlock or property name' }
if ($Like.Length) {
foreach ($Pattern in $Like) {
if ($Value -like $Pattern) { return }
}
}
if ($Regex.Length) {
foreach ($Pattern in $Regex) {
if ($Value -match $Pattern) { return }
}
}
return $_
}
don't use -notLike, -notMatch with Regular-Expression works in one line:
Get-MailBoxPermission -id newsletter | ? {$_.User -NotMatch "NT-AUTORIT.*|.*-Admins|.*Administrators|.*Manage.*"}
Easiest way I find for multiple searches is to pipe them all (probably heavier CPU use) but for your example user:
Get-EventLog -LogName Security | where {$_.UserName -notlike "*user1"} | where {$_.UserName -notlike "*user2"}
Scenario:
List all computers beginning with XX1 but not names where 4th character is L or P
Get-ADComputer -Filter {(name -like "XX1*")} | Select Name | Where {($_.name -notlike "XX1L*" -and $_.name -notlike "XX1P*")}
You can also count them by enclosing the above script in parens and adding a .count method like so:
(Get-ADComputer -Filter {(name -like "XX1*")} | Select Name | Where {($_.name -notlike "XX1L*" -and $_.name -notlike "XX1P*")}).count
Using select-string:
Get-EventLog Security | where {$_.UserName | select-string -notmatch user1,user2}
$listOfUsernames = #("user1", "user2", "etc", "and so on")
Get-EventLog -LogName Security |
where { $_.Username -notmatch (
'(' + [string]::Join(')|(', $listOfUsernames) + ')') }
It's a little crazy I'll grant you, and it fails to escape the usernames (in the unprobable case a username uses a Regex escape character like '\' or '(' ), but it works.
As "slipsec" mentioned above, use -notcontains if possible.