Powershell Where-Object inserting array - arrays

I'm trying to build a Powershell script that can filter the information from a CSV file. But I'm having problems when using an array as one of parameters.
This command works when I import the CSV file and type it in manually:
$data |
where-object {
(
$_.PackageAffected -eq "BslEqipWebPkg" -or
$_.PackageAffected -eq "BslIDealAdminPkg" -or
$_.PackageAffected -eq "ALL"
) -and (
$_.ENV -eq "PROD" -or $_.ENV -eq "ALL"
)
} |
select-object -property 'major step id','minor step id','PackageAffected',ENV,detail |
format-table -autosize
I am trying to build the $_.PackageAffected line up using an array.
Here is the code so far (that isn't working):
$packageList = "`$_.PackageAffected -eq `"" + $package[0] + "`" -or"
for ($element=1; $element -lt $package.count; $element++) {
$packageList = $packageList + " `$_.PackageAffected -eq `"" + $package[$element] + "`" -or"
}
$packageList = $packageList + " `$_.PackageAffected -eq `"ALL`""
$data |
where-object{($packageList) -and ($_.ENV -eq $environment -or $_.ENV -eq "ALL")} |
select-object -property 'major step id','minor step id','PackageAffected',ENV,detail |
format-table -autosize
$packageList
At the bottom where I've put $packageList it displays onscreen the string I would expect to see, which is:
$_.PackageAffected -eq "pkgName1" -or $_.PackageAffected -eq "pkgName2" -or $_.PackageAffected -eq "ALL"
But when I view the table after it's been run, there are no filters on it for the PackageAffected.
My suspicions are that I've converted the command into a string and when it runs, Powershell doesn't recognise it as a command.

You need to evaluate the expression. This can be done with Invoke-Expression, e.g.
Invoke-Expression $packageList
In the context of your code, this would be:
$data |
where-object{(Invoke-Expression $packageList) -and ($_.ENV -eq $environment -or $_.ENV -eq "ALL")} |
select-object -property 'major step id','minor step id','PackageAffected',ENV,detail |
format-table -autosize

Related

Scanning computers from Active Directory for Local Admin accounts

I need to scan Active Directory and pipe the information into a local admin checking script. But for some reason this script keeps failing. It fails at
$Group = $computer.psbase.children.find(”Administrators”)
Here is the script
################################################
#Start looking for windows 10 & computers 7 #
################################################
$root = {Get-ADComputer -Filter * | Where-Object {$_.Name -like "win10*"} | Select -Property Name
Get-ADComputer -Filter * | Where-Object {$_.Name -like "*win7"} | Select -Property Name}
foreach ($server in $root)
{
$computer = [ADSI](”WinNT://” + "$server" + “,computer”)
$Group = $computer.psbase.children.find(”Administrators”)
$members = ($Group.psbase.invoke(”Members”) | %{$_.GetType().InvokeMember(”Adspath”, ‘GetProperty’, $null, $_, $null)}) - replace ('WinNT://DOMAIN/' + $server + '/'), '' -replace ('WinNT://DOMAIN/', 'DOMAIN\') -replace ('WinNT://', '')
$members }
You were looking at the wrong line. It's the $members = line that it's complaining about. You have two spaces between the hyphen and your first "replace":
- replace ('WinNT://DOMAIN/' + $server + '/')
There shouldn't be any spaces there. Try this:
$root = {Get-ADComputer -Filter * | Where-Object {$_.Name -like "win10*"} | Select -Property Name
Get-ADComputer -Filter * | Where-Object {$_.Name -like "*win7"} | Select -Property Name}
foreach ($server in $root)
{
$computer = [ADSI](”WinNT://” + "$server" + “,computer”)
$Group = $computer.psbase.children.find(”Administrators”)
$members = ($Group.psbase.invoke(”Members”) | %{$_.GetType().InvokeMember(”Adspath”, ‘GetProperty’, $null, $_, $null)}) -replace ('WinNT://DOMAIN/' + $server + '/'), '' -replace ('WinNT://DOMAIN/', 'DOMAIN\') -replace ('WinNT://', '')
$members
}
If you aren't already, try using the Windows PowerShell ISE, which is included in Windows, for writing PowerShell scripts. It will highlight simple syntax errors like that.

Parsing an array of ojects into strings in PowerShell

I am calling Get-AdComputer like this:
[string[]] $computer = Get-ADComputer -properties * -filter {(operatingsystem -like "*Windows 7*")} |
Where-Object {$_.name -like "*-*"} |
Where-Object {$_.name -NotLike "V7-*"} |
Where-Object {$_.name -NotLike "*-NONE"} |
Where-Object {$_.name -NotLike "*-ONCALL"} |
Where-Object {$_.name -NotLike "*-BLACKBAUD"} |
Where-Object {$_.name -NotLike "SC-WIN7-1"} |
Where-Object {$_.name -NotLike "UT-SWCLIENT-01"} |
Select-Object Name, LastLogonDate
This creates a string array and I can access it like this:
$computer[1]
which returns
#{Name=W7-9HQPR3J; LastLogonDate=05/08/2017 09:45:13}
I need to output the data to a csv file in two columns Name, LastLogonDate
Unfortunately when I call $Computer | out-file -filepath $ServiceTagsPath -Force -Width 200 -Encoding ascii
I get one column with the headings on each line:
My other requirement is to be able to use the Name column in web-service calls to get warranty information...
so how could I do that?
I would not cast the output to array of strings [string[]] but leave the casting to powershell.
Then you can use command Export-Csv which works better with array of objects.
$computer = Get-ADComputer -properties * -filter {(operatingsystem -like "*Windows 7*")} |
Where-Object {$_.name -like "*-*"} |
Where-Object {$_.name -NotLike "V7-*"} |
Where-Object {$_.name -NotLike "*-NONE"} |
Where-Object {$_.name -NotLike "*-ONCALL"} |
Where-Object {$_.name -NotLike "*-BLACKBAUD"} |
Where-Object {$_.name -NotLike "SC-WIN7-1"} |
Where-Object {$_.name -NotLike "UT-SWCLIENT-01"} |
Select-Object Name, LastLogonDate
$computer | Export-Csv "test.csv" -NoTypeInformation

POWERSHELL - Using an array with a Foreach loop, looking at users in an OU -

Basicly i want to find inactive users so im using a foreach loop in a search base,
Now im trying to impliment looking at several OU's winthin an array at the start.
So this is what i was trying...
$OU=#("OU Path 1",
"OU Path 2",
"OU Path 3")
$OU | ForEach ($user in (Get-ADUser -SearchBase $_ -filter {(lastlogondate -notlike "*" -OR lastlogondate -le $90days) -AND (passwordlastset -le $90days) -AND (enabled -eq $True)} -Properties lastlogondate, passwordlastset | Select-Object name, lastlogondate, passwordlastset, samaccountname))
{
....
and getting the error....
At line:18 char:22
+ $OU | ForEach ($user in (Get-ADUser -SearchBase $_ -filter {(lastlogondate -notl ...
+ ~~ Unexpected token 'in' in expression or statement. At line:18 char:21
+ $OU | ForEach ($user in (Get-ADUser -SearchBase $_ -filter {(lastlogondate -notl ...
+ ~ Missing closing ')' in expression. At line:18 char:293
+ ... samaccountname))
+ ~ Unexpected token ')' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken
This Works Fine
$SearchBase1 = "OU1"
$SearchBase2 = "OU2"
$SearchBase3 = "OU3"
ForEach ($user in(Get-ADUser -SearchBase $SearchBase1 -filter {(lastlogondate -notlike "*" -OR lastlogondate -le $90days) -AND (passwordlastset -le $90days) -AND (enabled -eq $True)} -Properties lastlogondate, passwordlastset | Select-Object name, lastlogondate, passwordlastset, samaccountname))
{
The problem is that you are trying to created a nested ForEach loop, and forgot the first part of the loop. Here, you want to pipe $OU into a ForEach loop, and within that ForEach loop create another loop based on the current object of the first ForEach loop.
$OU | ForEach{
ForEach ($user in (Get-ADUser -SearchBase $_ -filter {(lastlogondate -notlike "*" -OR lastlogondate -le $90days) -AND (passwordlastset -le $90days) -AND (enabled -eq $True)} -Properties lastlogondate, passwordlastset | Select-Object name, lastlogondate, passwordlastset, samaccountname))
{
<Do Stuff>
} #End inner ForEach Loop for current OU
}

Powershell - combining arrays

I am new to powershell and in need of help. My first script for work is automate the new and termed users in AD environment.
A CSV dump will be done once daily from our Peoplesoft system. I use Import-CSV and create 3 arrays (new, term and processed).
The trouble I'm having is with combining the 3 arrays once i loop through all the users and try putting it back into the file. The code breaks at the $New += $Term lines. I believe this is due to the fact that there is only 1 record of each user type (new, term and processed) in my test file (I know, add more users…can't. This may be a real world outcome for any particular day). Below is my sample code:
#Get Credentials from user
$c = Get-Credential
#Get Date for $Term array population
$e = Get-Date -format M/d/yyyy
#Set file location and variable for said file
$File = "c:\users\nmaddux\desktop\adduserstuff\test.csv"
#Import record sets for New and Term users
$New = #()
$Term = #()
$Procd = #()
$New = Import-Csv $File | Where-Object {
$_.TermDate -eq "" -and $_.LastName -ne "" -and $_.Processdate -eq ""
}
$Term = Import-Csv $File | Where-Object {
$_.TermDate -ne "" -and $_.Processdate -eq "" -and $_.TermDate -le $e
}
$Procd = Import-Csv $File | Where-Object { $_.Processdate -ne "" }
#Process both new and term users provided there are records to process for each
If ($New -ne $NULL -and $Term -ne $NULL) {
# Some code to process users
}
$new += $term
$new += $Procd
$new | Export-Csv $file -NoTypeInformation -ErrorAction SilentlyContinue
So it will export but only partial results.
error - Method invocation failed because [System.Management.Automation.PSObject] doesn't contain a method named 'op_Addition'.
If Import-Csv only returns 1 result, then you are correct that your variable is assumed NOT to be an array, then concatenation will fail. This is not change by the fact that you have pre-initialized your variables with #(). In fact, that step isn't necessary.
To force the result to be treated as an array, you can either wrap your whole Import-Csv line in #(), or do something similar afterward.
$new = #( Import-Csv $File | Where-Object {...} )
# or
$new = Import-Csv $File | Where-Object {...}
$new = #($new)
So you are importing the same CSV file 3 times? isn't it better to import it once and then set the arrays to be filtered "views" of it?
Sort of like this. You should also be able to use the "Count" value from each array as well to say whether 1 or more results were returned.
#Get Credentials from user
$c = Get-Credential
#Get Date for $Term array population
$e = Get-Date -format M/d/yyyy
#Set file location and variable for said file
$File = "c:\users\nmaddux\desktop\adduserstuff\test.csv"
#Import record sets for New and Term users
[array]$New
[array]$Term
[array]$Procd
[array]$Import = Import-Csv $File
[array]$New = $Import | ? {$_.TermDate -eq "" -and $_.LastName -ne "" -and $_.Processdate -eq ""}
[array]$Term = $Import | ? {$_.TermDate -ne "" -and $_.Processdate -eq "" -and $_.TermDate -le $e}
[array]$Procd = $Import | ? {$_.Processdate -ne ""}
#Process both new and term users provided there are records to process for each
if (($New.Count -gt 0) -and ($Term.Count -gt 0))
{
# Some code to process users
}
$new += $term
$new += $Procd
$new | Export-Csv $file -NoTypeInformation -ErrorAction SilentlyContinue
You can also enforce the type by typecasting the variable:
$array = #()
$array = gci test.txt
$array.GetType()
[array]$array = #()
$array = gci test.txt
$array.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True FileInfo System.IO.FileSystemInfo
True True Object[] System.Array
I know I'm coming into this discussion late, but for someone else that comes along...
Since you already defined $new as an empty array, when you import from the csv you want to ADD the output to your pre-defined array, not set it equal to the output of import-csv.
$new = #()
$new += Import-Csv $File | Where-Object {
$_.TermDate -eq "" -and $_.LastName -ne "" -and $_.Processdate -eq ""
}

Compare Patch Levels across Multiple servers

Hi all I was working on a script to compare a list of patch levels between multiple servers and show the list of patches missing on either of server. The script should compare between each server within the array and give the output i was trying using Get-Hotfix and also using compare-object to compare and get the server name evaluating $_.sideindicator -match "=>" and $_.sideindicator -match "<=".
can anyone please help further?
here's the code till now for four servers, if there are n number of servers i wanted the logic on how to proceed.
$array=#()
$serd1 = Get-HotFix -ComputerName serd1 | select -ExpandProperty hotfixid
$serd2 = Get-HotFix -ComputerName serd2 | select -ExpandProperty hotfixid
$serd3 = Get-HotFix -ComputerName serd3 | select -ExpandProperty hotfixid
$serd4 = Get-HotFix -ComputerName serd4 | select -ExpandProperty hotfixid
$check1 = Compare-Object -ReferenceObject $serd1 -DifferenceObject $serd2 -IncludeEqual | ?{$_.sideindicator -notmatch '=='}
$array += $check1 | ?{$_.sideindicator -match "=>"} | Select-Object #{l="HostName";e={"serd1"}},#{l="MissingPatches";e={$_.inputobject}}
$array += $check1 | ?{$_.sideindicator -match "<="} | Select-Object #{l="HostName";e={"serd2"}},#{l="MissingPatches";e={$_.inputobject}}
$check2 = Compare-Object -ReferenceObject $serd1 -DifferenceObject $serd3 -IncludeEqual | ?{$_.sideindicator -notmatch '=='}
$array += $check2 | ?{$_.sideindicator -match "=>"} | Select-Object #{l="HostName";e={"serd1"}},#{l="MissingPatches";e={$_.inputobject}}
$array += $check2 | ?{$_.sideindicator -match "<="} | Select-Object #{l="HostName";e={"serd3"}},#{l="MissingPatches";e={$_.inputobject}}
$check3 = Compare-Object -ReferenceObject $serd1 -DifferenceObject $serd4 -IncludeEqual | ?{$_.sideindicator -notmatch '=='}
$array += $check3 | ?{$_.sideindicator -match "=>"} | Select-Object #{l="HostName";e={"serd1"}},#{l="MissingPatches";e={$_.inputobject}}
$array += $check3 | ?{$_.sideindicator -match "<="} | Select-Object #{l="HostName";e={"serd4"}},#{l="MissingPatches";e={$_.inputobject}}
$check4 = Compare-Object -ReferenceObject $serd2 -DifferenceObject $serd3 -IncludeEqual | ?{$_.sideindicator -notmatch '=='}
$array += $check4 | ?{$_.sideindicator -match "=>"} | Select-Object #{l="HostName";e={"serd2"}},#{l="MissingPatches";e={$_.inputobject}}
$array += $check4 | ?{$_.sideindicator -match "<="} | Select-Object #{l="HostName";e={"serd3"}},#{l="MissingPatches";e={$_.inputobject}}
$check5 = Compare-Object -ReferenceObject $serd2 -DifferenceObject $serd4 -IncludeEqual | ?{$_.sideindicator -notmatch '=='}
$array += $check5 | ?{$_.sideindicator -match "=>"} | Select-Object #{l="HostName";e={"serd2"}},#{l="MissingPatches";e={$_.inputobject}}
$array += $check5 | ?{$_.sideindicator -match "<="} | Select-Object #{l="HostName";e={"serd4"}},#{l="MissingPatches";e={$_.inputobject}}
$check6 = Compare-Object -ReferenceObject $serd3 -DifferenceObject $serd4 -IncludeEqual | ?{$_.sideindicator -notmatch '=='}
$array += $check6 | ?{$_.sideindicator -match "=>"} | Select-Object #{l="HostName";e={"serd3"}},#{l="MissingPatches";e={$_.inputobject}}
$array += $check6 | ?{$_.sideindicator -match "<="} | Select-Object #{l="HostName";e={"serd4"}},#{l="MissingPatches";e={$_.inputobject}}
$array
The question is how to make this script work for random number of servers
This question is old but thought this might help out if anyone else finds this and thinks it is something they'd want to give a go.
Anyway, to meet the requirements stated here, see if this helps - let me know if anyone finds problems:
$Servers = "SERVER1", "SERVER2", "SERVER3", "SERVER4"
$MissingPatches=#()
$ServerPatches = New-Object 'object[]' $($Servers.Length)
for($i=0; $i -lt $($Servers.Length); $i++)
{
$ServerPatches[$i] = Get-HotFix -ComputerName $Servers[$i] | select -ExpandProperty hotfixid
}
for($i=0; $i -lt $($Servers.Length); $i++)
{
for($j=($i+1); $j -lt $($Servers.Length); $j++)
{
$Compare = Compare-Object -ReferenceObject $ServerPatches[$i] -DifferenceObject $ServerPatches[$j] -IncludeEqual | ?{$_.sideindicator -notmatch '=='}
$MissingPatches += $Compare | ?{$_.sideindicator -match "=>"} | Select-Object #{l="HostName";e={$Servers[$i]}},#{l="MissingPatches";e={$_.inputobject}}
$MissingPatches += $Compare | ?{$_.sideindicator -match "<="} | Select-Object #{l="HostName";e={$Servers[$j]}},#{l="MissingPatches";e={$_.inputobject}}
}
}
$MissingPatches = foreach($MissingPatch in $MissingPatches) {
if($($MissingPatch.MissingPatches) -ne "File 1")
{
$MissingPatch
}
}
$MissingPatches | ft -AutoSize
Going down this path I started thinking of potential improvements. So, if I have time I might rewrite and create a patch object that indicates on what server it is present and where it is missing for more detail of why a patch is indicated as "Missing".
When a patch is found on any server it is added to that array. A property of PresentOn will be updated with the server name it was found and the servers so far that didn't contain the patch can be added to a property of MissingOn, then checked continuously going forward. Then, a table of all patches can be displayed by these statuses vs. printing out only a "Missing" array. Could take some doing but might be worth the work. I'm also seeing this being a potentially costly script based on the number of patches in play and servers. But, if anyone else gets to it first (or finds flaws in the approach) let me know here!
This should work:
$servers = "one", "two", "three"
$array = #()
for ($i = 0; $i -lt ($servers.Count - 1); $i++)
{
$serd1 = Get-HotFix -ComputerName $servers[$i] | select -ExpandProperty hotfixid
$serd2 = Get-HotFix -ComputerName $servers[$i+1] | select -ExpandProperty hotfixid
$array += $check1 | ?{$_.sideindicator -match "=>"} | Select-Object #{l="HostName";e={"serd1"}},#{l="MissingPatches";e={$_.inputobject}}
$array += $check1 | ?{$_.sideindicator -match "<="} | Select-Object #{l="HostName";e={"serd2"}},#{l="MissingPatches";e={$_.inputobject}}
}
$array

Resources