I have an array with exclusions like:
$hosts= #("*iphone*","*Samsung*")
I would like to exclude this hosts from my dhcp-leases like:
Get-DhcpServerv4Lease -ComputerName $dhcp_server -ScopeId $scope.ScopeID |ForEach-Object {
if(($_) -and $_.Hostname -notlike $hostnames -and $_.ClientID -notlike $clientids -and $_.Description -notlike $descriptions) {
do something
When I work without array like:
$leases += Get-DhcpServerv4Lease -ComputerName $dhcp_server -ScopeId $scope.ScopeID |
where {$_.Hostname -notlike "*iphone*" -and $_.Hostname -notlike...
this works already fine.
So I've no idea, why this does not work when the exclusions are in an array instead of the where clause
AFAIK you should not use -notlike to compare against an array.
You can do it using the Regular Expression operator -notmatch. For that, you combine the options in the array with the regex OR character |.
$hosts= ("iphone","Samsung" | ForEach-Object { [Regex]::Escape($_) }) -join '|'
results in iphone|Samsung.
Note the use of [Regex]::Escape() to make sure Regex special characters get escaped so they are seen as literals.
With that, use a Where-Object clause like this:
Where-Object {$_.Hostname -notmatch $hosts}
Do the same for $clientids and $descriptions
Related
I am having code where I am searching VM details using Get-VM -Name $VM_Name.
It is not giving any output in below case where I have 2 entries for same VM name like below
001_MBM1P
001_MBM1P_Clone_Prj_Win
was trying something like blow but getting error
get-vm $VM_Name | where {($_.Name -match $VM_Name) -and ($VM_Name -notcontains 'Clone*')}
get-vm : 1/10/2022 11:36:52 AM Get-VM VM with name '001_MBM1P' was not found using the specified filter(s).
Please let me know how can I filter the search which will work in both cases.
The -contains and -notcontains operators work on arrays only, they are not string operations.
You may use the efficient String::Contains() method:
get-vm | where {($_.Name -match $VM_Name) -and (-not $_.Name.Contains('Clone'))}
Alternatively use the -notlike operator:
get-vm | where {($_.Name -match $VM_Name) -and ($_.Name -notlike '*Clone*')}
Note that Contains() is case-sensitive whereas -like and -notlike are not. This Q/A shows some ways for case-insensitive "contains" operation using String methods.
I am having trouble coming up with a way to do complex logic using the where-object in PowerShell
I have the following code, but separating the and from the ors is not working as intended.
Get-ADComputer -Filter * | Where-Object {($_.Enabled) -and
(($_.DistinguishedName -Contains "world") -or
($_.DistinguishedName -Contains "foo")) -and
(($_.SID -Contains "bar") -or
($_.SID-Contains "something else"))}
If I do this in c# I get results, but in powershell I do not.
Any thoughts on how to get around this?
TIA
This is how you would perform your query using AD PS Module:
Get-ADComputer -LDAPFilter "(!userAccountControl:1.2.840.113556.1.4.803:=2)"|where-object{
$_.DistinguishedName -match 'World|Foo' -and $_.SID -match 'bar|something else'
}
-LDAPFilter "(!userAccountControl:1.2.840.113556.1.4.803:=2)" = Enabled Computer Object
-match allows the use of regex, you can use the pipeline as OR.
-contains is the operator you would use to lookup an item on an array. Example:
PS /> #(
'apple'
'banana'
'pineapple'
) -contains 'apple'
True
In addition, as Dave Wyatt pointed out in a nice post on powershell.org a while ago, you might want to avoid where-object whenever possible since it's the slowest way to filter collections. It's only advantages are the low memory consumption and pipeline streaming.
Here are a few examples of faster efficient code:
#Example 1:
$computers=Get-ADComputer -LDAPFilter "(!userAccountControl:1.2.840.113556.1.4.803:=2)"
$collection=[system.collections.generic.list[Microsoft.ActiveDirectory.Management.ADComputer]]::new()
foreach($computer in $computers)
{
if($computer.DistinguishedName -match 'World|Foo' -and $computer.SID -match 'bar|something else')
{
$collection.add($computer)
}
}
#Example 2:
filter myFilter{
if($_.DistinguishedName -match 'World|Foo' -and $_.SID -match 'bar|something else')
{
$_
}
}
$computers=Get-ADComputer -LDAPFilter "(!userAccountControl:1.2.840.113556.1.4.803:=2)"|myFilter
#Example 3
$computers=(Get-ADComputer -LDAPFilter "(!userAccountControl:1.2.840.113556.1.4.803:=2)").where({
$_.DistinguishedName -match 'World|Foo' -and $_.SID -match 'bar|something else'
})
There is plenty on information on the different ways of filtering Collections on PowerShell and their pros / cons on Google.
this is not an Answer - so please let me know when you have read it so that i can delete it.
this is your code with more informative indentation, with the needless extra parens removed, and with spaces around the operators.
Get-ADComputer -Filter * |
Where-Object {
$_.Enabled -and
($_.DistinguishedName -Contains "world" -or
$_.DistinguishedName -Contains "foo") -and
($_.SID -Contains "bar" -or
$_.SID -Contains "something else")
}
note that the -contains operator is NOT for strings ... it is for membership in a collection. if you want to test against strings, use -match, or .Contains(), or -like with wildcards.
When I execute below code, I get an error as : A parameter cannot be found that matches name or. How can I get user list who don't have abc.com or xyz.com in their email address?
Get-ADGroupMember -Identity "AQ" -Recursive | where objectClass -eq 'user' | Get-ADUser -Properties *, "msDS-UserPasswordExpiryTimeComputed", PasswordNeverExpires |
where mail -notmatch "#abc.com" -or "#xyz.com" |
Select-Object #{Label = "SAM Account Name";Expression = {$_.SamAccountName}}
The curly braces (actually a scriptblock with the filter script) can not always be skipped with Where-Object.
You can do Where-Object objectClass -eq 'user' but everything that involves more than a single operator requires to be written as a filter script:
where {$_.mail -notmatch "#abc.com" -or "#xyz.com" }
Now this logic doesn't work, as this is equivalent to the following statement:
where {($_.mail -notmatch "#abc.com") -or $true }
So your where clause is true, regardless of the result of the -notmatch operation. You want two -notmatch operations instead:
Where-Object - { $_.Mail -notmatch '#abc.com' -and $_.Mail -notmatch '#xyz.com' }
Depending on the amount of email addresses, that you want to exclude in your filter script, you might want to use a different approach: Strip the user name from the email address and see, if this address appears in the array of email addresses that you want to exclude.
Where-Object { ( $_.Mail -replace '^[^#]+') -notin '#abc.com','#xyz.com','#foo.bar' }
for multiple conditions use full syntax:
where-object { $_.property -eq $b -and $_.otherproperty -match $a }
You're missing some brackets around your where-clause:
where {objectClass -eq 'user'}
And this:
where {mail -notmatch "#abc.com" -or "#xyz.com"}
Should look like that:
where {mail -notmatch "#abc.com" -or mail -notmatch "#xyz.com"}
Please rethink the logic of your second where since it will always be true.
I can't seem to figure out how to use a variable in a herestring, and for the variable to be expanded at a later time in a piped command. I have experimented with single ' and double " quotes, and escape ` characters.
I am trying to use the herestring for a list (e.g. like an array) of Exchange groups, and a corresponding list of conditions to apply to those groups. Here is a simplified example which fails to use the $Conditions variable correctly (it does not expand the $_.customattribute2 variable):
# List of groups and conditions (tab delimitered)
$records = #"
Group1 {$_.customattribute2 -Like '*Sales*'}
Group2 {$_.customattribute2 -Like '*Marketing*' -OR $_.customattribute2 -Eq 'CEO'}
"#
# Loop through each line in $records and find mailboxes that match $conditions
foreach ($record in $records -split "`n") {
($DGroup,$Conditions) = $record -split "`t"
$MailboxList = Get-Mailbox -ResultSize Unlimited
$MailboxList | where $Conditions
}
No, no that's not going to work. The whole good bit about PowerShell is not having to make everything a string and then drag it to the moon and back trying to get the important stuff back out of the string. {$_.x -eq "y"} is a scriptblock. It's a thing by itself, you don't need to put it in a string.
#Array of arrays. Pairs of groups and conditions
[Array]$records = #(
('Group1', {$_.customattribute2 -Like '*Sales*'}),
('Group2', {$_.customattribute2 -Like '*Marketing*' -OR $_.customattribute2 -Eq 'CEO'})
)
#Loop through each line in $records and find mailboxes that match $conditions
foreach ($pair in $records) {
$DGroup, $Condition = $pair
$MailboxList = Get-Mailbox -ResultSize Unlimited
$MailboxList | where $Condition
}
TessellatingHeckler's explanation is right. However, if you insist on herestring, it's possible as well. See the following example (created merely for demonstration):
$records=#'
Group1 {$_.Extension -Like "*x*" -and $_.Name -Like "m*"}
Group2 {$_.Extension -Like "*p*" -and $_.Name -Like "t*"}
'#
foreach ($record in $records -split "`n") {
($DGroup,$Conditions) = $record -split "`t"
"`r`n{0}={1}" -f $DGroup,$Conditions
(Get-ChildItem |
Where-Object { . (Invoke-Expression $Conditions) }).Name
}
Output:
PS D:\PShell> D:\PShell\SO\47108347.ps1
Group1={$_.Extension -Like "*x*" -and $_.Name -Like "m*"}
myfiles.txt
Group2={$_.Extension -Like "*p*" -and $_.Name -Like "t*"}
Tabulka stupnic.pdf
ttc.ps1
PS D:\PShell>
Caution: some text/code editors could convert tabulators to space sequences!
I need to filter out 4 machines prior to exporting to a csv file. I have no clue how to filter them out. I tried the IF clause but this produced nothing. Please help.
$old = (Get-Date).AddDays(-90) # Modify the -90 to match your threshold
$oldComputers = Get-ADComputer -SearchBase "OU=Workstations,DC=Corporate,DC=Local" -SearchScope 2 -Filter { PasswordLastSet -le $old } -Properties *
$oldComputers
if ($_.name -notlike "1919DD" -or $_.name -notlike "1919SMAHESHWARE" -or $_.name -notlike "1919IETEST" -or $_.name -notlike "1920BPASCERITB") {
Export-Csv c:\temp\Over90DaysMachines.csv -NoTypeInformation -Force -Append
}
For one thing, to be able to use the current object variable ($_) you need a pipeline context. Simply putting an if statement after echoing a variable doesn't automagically feed the echoed value(s) into the if statement. You need to change this:
$oldComputers
if ($_.Name -notlike "1919DD" -or ...) {
Export-Csv c:\temp\Over90DaysMachines.csv -NoTypeInformation -Force -Append
}
into something like this:
$oldComputers | Where-Object {
$_.Name -notlike "1919DD" -or ...
} | Export-Csv c:\temp\Over90DaysMachines.csv -NoType -Force
However, even with that change your filter won't work correctly, because you connected the -notlike clauses via -or when you should have used -and. You obviously meant to process objects only if their name doesn't match any of the given values. But for your logical expression to evaluate to $false the name would have to match all of the reference value at the same time. Which clearly isn't possible, thus your expression always evaluates to $true.
Example:
Assume that you have a variable $v that should not be equal to either A, B, or C. Applying your logic, the expression would look somewhat like this in PowerShell:
($v -notlike 'A') -or ($v -notlike 'B') -or ($v -notlike 'C')
If $v takes for instance the value A that expression becomes
('A' -notlike 'A') -or ('A' -notlike 'B') -or ('A' -notlike 'C')
⇔ ($false) -or ($true) -or ($true)
⇔ $true
To check if a give value equals neither of the reference values you need to connect the clauses via -and:
('A' -notlike 'A') -and ('A' -notlike 'B') -and ('A' -notlike 'C')
⇔ ($false) -and ($true) -and ($true)
⇔ $false
$oldComputers | Where-Object {
$_.Name -notlike "1919DD" -and
$_.Name -notlike "1919SMAHESHWARE" -and
$_.Name -notlike "1919IETEST" -and
$_.Name -notlike "1920BPASCERITB"
} | Export-Csv c:\temp\Over90DaysMachines.csv -NoType -Force
Note BTW, that the -notlike operator behaves exactly like the -ne operator when the reference string doesn't contain wildcard characters. If you're not doing fuzzy matches anyway you could simplify your expression by checking if the given name is (not) found in an array of names instead of doing multiple checks for (in)equality:
$excludes = '1919DD', '1919SMAHESHWARE', '1919IETEST', '1920BPASCERITB'
$oldComputers | Where-Object {
$excludes -notcontains $_.Name
} | Export-Csv c:\temp\Over90DaysMachines.csv -NoType -Force
Another option would be a regular expression (non-)match:
$oldComputers | Where-Object {
$_.Name -notmatch '^1919DD|1919SMAHESHWARE|1919IETEST|1920BPASCERITB$'
} | Export-Csv c:\temp\Over90DaysMachines.csv -NoType -Force
I'm guessing the code in the OP is a fragment from a larger script. Presumably it is the body or part of the body of a ForEach-Object. (If not then $_ doesn't make sense in this context). However a ForEach-Object isn't necessary. You can filter out the unwanted computers as follows:
$old = (Get-Date).AddDays(-90) # Modify the -90 to match your threshold
$oldComputers = Get-ADComputer -SearchBase "OU=Workstations,DC=Corporate,DC=Local" -SearchScope 2 -Filter { PasswordLastSet -le $old } -Properties *
$oldComputers | Where-Object {
$_.name -notin "1919SMAHESHWARE","1919IETEST", "1920BPASCERITB"
} | Export-Csv c:\temp\Over90DaysMachines.csv -NoTypeInformation -Force -Append
This assumes that $oldComputers is an array of object where each object has a property name and the value of name is a string like "server1", "server2", etc. The script in the OP outputs $oldComputers so verify it looks like a set of objects, with a name property consisting of a string where the servers to be excluded are spelled exactly as listed in the OP.
Please try below code
$old = (Get-Date).AddDays(-90) # Modify the -90 to match your threshold
$oldComputers = Get-ADComputer -searchbase "OU=Workstations,DC=Corporate,DC=Local" -SearchScope 2 -Filter { PasswordLastSet -le $old } -Properties *
$oldComputers = $oldComputers | where {$_.name -notlike "1919DD"
` -or $_.name -notlike "1919SMAHESHWARE"
` -or $_.name -notlike "1919IETEST"
` -or $_.name -notlike "1920BPASCERITB"}
Export-Csv c:\temp\Over90DaysMachines.csv -NoTypeInformation -force -append