I have a string array $Exclude which I want to put into a filter in a Get-WMIObject cmdlet. I have added it at the end but it does not work.
How can I filter out the services that are listed in that array?
$ServicesToExclude = "RemoteRegistry,CpqNicMgmt"
$Exclude = $ServicesToExclude.split(",")
$Services = Get-WmiObject -Class Win32_Service -Filter {State != 'Running' and StartMode = 'Auto' and Name -ne $Exclude}
$Result = foreach ($Service in $Services.Name)
{
Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$Service" |
Where-Object {$_.Start -eq 2 -and $_.DelayedAutoStart -ne 1}|
Select-Object -Property #{label='ServiceName';expression={$_.PSChildName}} |
get-Service
}
If ($Result.count -gt 0){
$Displayname = $Result.displayname
[string] $Line = "`n-----------------------------------------"
$Api.LogScriptEvent( 'Stopped_Auto_Services.ps1',1234,4,"`nStopped Automatic Services$Line `n$($Displayname)")
Filtering an array out of a list is not done on the WMI side. Instead, you should use Where-Object to filter out those services which name is contained in $Exclude.
$Services = Get-WmiObject -Class Win32_Service -Filter {State != 'Running' and StartMode = 'Auto'} |
Where-Object {$Exclude -notcontains $_.Name}
WMI queries do not work well with arrays and need to be done a different way. If you want to keep the filtering on the server side, you can do some work prior to running the command by creating a filter string as shown here:
$Exclude = "RemoteRegistry","CpqNicMgmt"
$StringBuilder = New-Object System.Text.StringBuilder
[void]$StringBuilder.Append("State != 'Running' AND StartMode = 'Auto' AND ")
[void]$StringBuilder.Append("($(($Exclude -replace '^(.*)$','Name != "$1"') -join ' AND '))")
$Query = $StringBuilder.ToString()
$Services = Get-WmiObject -Class Win32_Service -Filter $Query
There may be better ways to accomplish this, but this was the first thing that I could think of to accomplish the goal of your question.
Related
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.
I need to identify if the installed software (several times per host) is an 32bit or 64bit version. To do this I want to check the Execution Folder of the Service via powershell.
This is my first powershell script and I'm a bit lost. I would like to store the information of Get-WmiObject win32_service to a multidimensional array.
If I run the command selecting PathName, State and DisplayName the PathName will be shortened, for that I run this command several times. But don't know how to get is in the right fields of the array or get the right fields in my foreach
Here is what I got so far:
`$ServiceArray = #()
$ServiceArray[] = Get-WmiObject win32_service | ?{$_.Name -like 'foo_*'} |
Select PathName
$ServiceArray[][] = Get-WmiObject win32_service | ?{$_.Name -like 'foo_*'} |
Select State
$ServiceArray[][][] = Get-WmiObject win32_service | ?{$_.Name -like 'foo_*'}
| Select DisplayName
foreach($array in $ServiceArray[])
{
if ($array.Contains(\bin\test\win64\test.exe)
{
$ServiceArray[][][][] = "win64"
}
else
{
$ServiceArray[][][][] = "win32"
} `
I know that it does not work this way, but I don't know how it works correct, either.
You can select multiple properties in the same statement with Select-Object:
$ServiceArray = Get-WmiObject Win32_Service |
Where-Object {$_.Name -like 'foo_*'} |
Select PathName,State,DisplayName
You can also use Select-Object with a calculated property to add the bitness based on the PathName argument if needed:
$ServiceArray = Get-WmiObject Win32_Service |
Where-Object {$_.Name -like 'foo_*'} |
Select PathName,State,DisplayName,#{Name='Bitness';Expression={if($_.PathName -like "*Win64*"){"Win64"}else{"Win32"}}}
You can select multiple properties. You were almost there. Edited your code a little.
$ServiceArray = #()
$ServiceArray = Get-WmiObject win32_service | ?{$_.Name -like 'foo_*'} | Select PathName,State,DisplayName,Architecture
foreach($Element in $ServiceArray)
{
if ($Element.PathName.Contains('\bin\test\win64\test.exe'))
{
$Element.Architecture = 'win64'
}
else
{
$Element.Architecture = 'win32'
}
$Element
}
I have been trying to get this script to get the external IP address of multiple machines on my network. So far, the script seems to iterate through the loop but run the command on my local machine rather than the remote one on the loop.
$computers = get-content "c:\scripts\scriptdev\Addresses.txt"
$outfile ="c:\scripts\scriptdev\test2.csv"
$results = #()
foreach ($computer in $computers)
{
Invoke-RestMethod http://ipinfo.io/json | Select -exp ip $computer
Get-WMIObject Win32_ComputerSystem | Select-Object -ExpandProperty name $computer
}
You need to use a cmdlet or parameter to specify remote execution. $computer by itself is just a variable with a string-value.
Some cmdlets support a -ComputerName $computer paramter, while others like Invoke-RestMethod require that you run them using Invoke-Command or something similar.
Ex.
$computers = get-content "c:\scripts\scriptdev\Addresses.txt"
$results = #()
foreach ($computer in $computers)
{
$results += Invoke-Command -HideComputerName -ComputerName $computer -ScriptBlock {
New-Object -TypeName psobject -Property #{
Name = Get-WMIObject Win32_ComputerSystem | Select-Object -ExpandProperty name
ExternalIP = Invoke-RestMethod http://ipinfo.io/json | Select -ExpandProperty ip
}
}
}
$results
So, I'm having an issue enumerating through a forEach loop in PowerShell (v3) and adding the variable being evaluated, as well as a Test-Connection result into an array. I'm trying to make $arrPing a multi-dimensional array as this will make it easier for me to filter and process the objects in there later in the script, but I'm encountering issues with the code.
My code looks like the following:
$arrPing= #();
$strKioskIpAddress= (Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object { $_.IPAddress -ne $null }).ipaddress
...FURTHER DOWN THE CODE...
$tmpIpAddress= Select-Xml -Path $dirKioskIpAddresses -XPath '//kiosks/kiosk' | Select-Object -ExpandProperty Node
forEach ( $entry in $tmpIpAddress )
{
if ( $entry -ne $strKioskIpAddress )
{
$result= Test-Connection -ComputerName $entry -Count 1 -BufferSize 16 -Quiet -ErrorAction SilentlyContinue
$arrPing+= #($entry,$result);
}
}
But I'm getting the following output when I display the contents of the $arrPing variable:
PS H:\Documents\PowerShell Scripts> $arrPing
10.216.1.134
True
10.216.1.139
True
10.216.23.230
True
10.216.23.196
False
10.216.23.23
False
Can anyone tell me where I'm going wrong? I have a feeling that this is happening because I'm in a forEach loop but I just can't say for sure...
I would simplify it a bit by using a PSCustomObject:
$Ping = foreach ($Entry in $tmpIpAddress) {
if ($Entry -ne $strKioskIpAddress) {
$TestParams = #{
ComputerName = $Entry
Count = '1'
BufferSize = '16'
Quiet = $true
ErrorAction = 'SilentlyContinue'
}
$Result = Test-Connection #TestParams
[PSCustomObject]#{
Entry = $Entry
Result = $Result
}
}
}
$Ping
To avoid a long row of parameters I've used a technique called splatting.
You are seeing how PowerShell unrolls arrays. The variable is as designed: a large array. However PowerShell, when displaying those, puts each element on its own line. If you do not want that and especially if you are going to use This data will be used to filter out computers which are not on the network then you should use PowerShell objects.
if ( $entry -ne $strKioskIpAddress ){
$objPing += New-Object -TypeName psobject -Property #{
Entry = $entry
Result = Test-Connection -ComputerName $entry -Count 1 -BufferSize 16 -Quiet -ErrorAction SilentlyContinue
}
}
Instead of that those I would continue and use a different foreach contruct which is more pipeline friendly. That way you can use other cmdlets like Export-CSV if you need this output in other locations. Also lie PetSerAl says
[Y]ou should not use array addition operator and add elements one by one. It [will] create [a] new array (as arrays are not resizable) and copy elements from [the] old one on each operation.
$tmpIpAddress | Where-Object{$_ -ne $strKioskIpAddress} | ForEach-Object{
New-Object -TypeName psobject -Property #{
Entry = $_
Result = Test-Connection -ComputerName $_ -Count 1 -BufferSize 16 -Quiet -ErrorAction SilentlyContinue
}
} | Export-CSV -NoTypeInformation $path
The if is redundant now that we have moved that logic into Where-Object since you were using it do filter out certain records anyway. That is what Where-Object is good for.
The above code is good for PowerShell 2.0. If you have 3.0 or later then use [pscutomobject] and [ordered]
$tmpIpAddress | Where-Object{$_ -ne $strKioskIpAddress} | ForEach-Object{
[psobject][ordered] #{
Entry = $_
Result = Test-Connection -ComputerName $_ -Count 1 -BufferSize 16 -Quiet -ErrorAction SilentlyContinue
}
} | Export-CSV -NoTypeInformation $path
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 ""
}