I want to add a specific selection of data in a variable like this:
$a = Get-Service | select -First 10 | ? Name -Like "App*"
$collection = $null
foreach($item in $a){
if($item.Status -like "Running"){
$collection = $collection + $item
}
}
When trying to run I got an error like:
No method found "op_addition"
What can I do to save my selection in a separate variable?
At first, $collection is $null.
Then, after the first loop iteration, $collection is a single ServiceController object, since $null + $object is just $object.
On the second loop iteration, it fails because ServiceController don't have any overloads for +, just as the error informs you.
You'll need to declare $collection an actual collection (you can use the #() array subexpression operator) for + to work they way you expect:
$a = Get-Service | select -First 10 | ? Name -Like "App*"
$collection = #()
foreach($item in $a){
if($item.Status -like "Running"){
$collection = $collection + $item
}
}
Alternatively, assign all the output from the loop directly to $collection:
$a = Get-Service | select -First 10 | ? Name -Like "App*"
$collection = foreach($item in $a){
if($item.Status -like "Running"){
$item
}
}
Which could of course be simplified with a single Where-Object statement in the pipeline:
$collection = Get-Service App* |Where-Object {$_.Status -eq 'Running'}
Related
I'm looking to do the Following Steps:
Reading in a CSV File
Add The Contents from the CSV File to An Array
Compare the Contents from the CSV with the Contents from another Array
If the Items in the Array aren't members of the CSV Array
Send Email
I First Tried Running the Script with One Host Missing from the Reports2 CSV File, This Missing Host was displayed to the Console and Written to the Reports2 File, but Still when i Re-Run the Code it still displays the last Element (Host that was Missing From Reports2.CSV):
This is the script I'm currently working On:
EDIT: I have now edited the code snippet to reflect the working solution
$user = ''
$pswd = ''
$vCenter_Servers = ""
$now = Get-Date
$start = $now.AddDays(-15)
$esxiHosts = Import-CSV C:\Scripts\Report1.csv #Reading CSV File
$MaitanceMode = #()
$Ticket = #()
foreach($ESXI in $esxiHosts){
$Ticket += $ESXI | Select-Object -ExpandProperty Name
}
foreach($vCenter in $vCenter_Servers) {
$srv = Connect-VIServer -Server $vCenter -User $user -Password $pswd
Get-VMHost -PipelineVariable esx -Server $srv | Where-Object {$_.ConnectionState -eq 'Maintenance'} |
ForEach-Object -Process {
$maintEntered = Get-VIEvent -Entity $esx -Start $start -MaxSamples ([int]::MaxValue) -Server $srv |
Where-Object{$_ -is [VMware.Vim.EnteredMaintenanceModeEvent]}
if($maintEntered){
#Skipping
}
else {
$MaitanceMode += $esx | Select-Object -ExpandProperty Name
}
}
} #Ending ForEach Loop
$NoTicket = $MaitanceMode | Where {$Ticket -Contains $_}
$NoTicket
You should instantiate your array containing the results as an empty array, probably before ForEach-Object -Process {... with $MaitanceMode = #() and when you want to add elements to it, replace this line:
$MaitanceMode = $esx | select name
by
$MaitanceMode += $esx | select name
Edit:
Further replace this line:
$esxiHosts = Import-CSV C:\Scripts\Report2.csv
by this line:
$esxiHosts = Import-CSV C:\Scripts\Report2.csv | Select-Object -ExpandProperty Name
and this line:
$MaitanceMode += $esx | select name
by this line:
$MaitanceMode += $esx | Select-Object -ExpandProperty Name
And don't forget to instantiate $MaitanceMode as an empty array. This is now mandatory. Otherwise it will become a string and not an array.
Despite the accepted answer from #Thomas, it is not correct to use the increase assignment operator (+=) to create a collection in PowerShell. For one thing, it is a very expensive syntax.
see: Why should I avoid using the increase assignment operator (+=) to create a collection.
To build a collection of objects in PowerShell, you should use the PowerShell pipeline by removing the <variable> += of the concerned commands (this will leave the objects on the pipeline) and catch the whole collection by adding <variable> = in front of the iterator (e.g. Foreach-Object). By using this PowerShell syntax, there is no need to initiate the arrays (<variable> = #()).
Taking your script as an example:
$user = ''
$pswd = ''
$vCenter_Servers = ""
$now = Get-Date
$start = $now.AddDays(-15)
$esxiHosts = Import-CSV C:\Scripts\Report1.csv #Reading CSV File
$Ticket = foreach($ESXI in $esxiHosts){
$ESXI | Select-Object -ExpandProperty Name
}
foreach($vCenter in $vCenter_Servers) {
$srv = Connect-VIServer -Server $vCenter -User $user -Password $pswd
Get-VMHost -PipelineVariable esx -Server $srv | Where-Object {$_.ConnectionState -eq 'Maintenance'} |
$MaitanceMode = ForEach-Object -Process {
$maintEntered = Get-VIEvent -Entity $esx -Start $start -MaxSamples ([int]::MaxValue) -Server $srv |
Where-Object{$_ -is [VMware.Vim.EnteredMaintenanceModeEvent]}
if($maintEntered){
#Skipping
}
else {
$esx | Select-Object -ExpandProperty Name
}
}
} #Ending ForEach Loop
$NoTicket = $MaitanceMode | Where {$Ticket -Contains $_}
$NoTicket
I am trying to find all last job statuses fro a list of VMs backed up with veeam backup.
Strangely the loop do not go to the next vm. Here is what I do:
Add-PSSnapin VeeamPSSnapin
$VMlist = "vm1, vm2"
$VMlist = $VMlist.split(",");
Foreach ($i in $VMlist) {
foreach($Job in (Get-VBRJob))
{
$Session = $Job.FindLastSession()
if(!$Session){continue;}
$Tasks = $Session.GetTaskSessions()
$Tasks | ?{$_.Name -eq $VMlist} | %{write-host $_.Name ":" $_.Status}
It seems I have a problem in the for each loop, since it stuck and I do not get any output. What is thebest way to iterate over the slit of VMs?
Thanks in advance!
You're looking for the $VMList array in $Tasks not the individual VM $i, just change: {$_.Name -eq $i}
Also your VM names will include leading spaces, either remove the spaces from your input string "vm1,vm2", or use Trim() after Split()
Add-PSSnapin VeeamPSSnapin
$VMlist = "vm1,vm2"
$VMlist = $VMlist.split(",");
foreach ($i in $VMlist) {
foreach ($Job in (Get-VBRJob)) {
$Session = $Job.FindLastSession()
if (!$Session) {continue; }
$Tasks = $Session.GetTaskSessions()
$Tasks | Where-Object {$_.Name -eq $i} | ForEach-Object {Write-Host $_.Name ":" $_.Status}
}
}
I'm just having problems trying to export the following to a CSV, I've tried putting the Export-CSV within the Foreach-Object loop to no avail.
I want to put the server name $server in the first column and the description $description in the second.
In an earlier version, I was able to make a text file using Out-File -append .\outa.txt -InputObject $_.Name,$description but the formatting did not work well.
$server = Get-ADComputer -Filter {OperatingSystem -Like "*Server*" -and Name -Notlike "*DOM*"}
$server | ForEach-Object {
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$_.Name)
$RegKey = $Reg.OpenSubKey("SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters")
$description = $RegKey.GetValue("srvcomment")
} | Export-Csv .\out1.csv
A ForEach as oppose to ForEach-Object:
$server = Get-ADComputer -Filter {OperatingSystem -Like "*Server*" -and Name -Notlike "*DOM*"}
foreach($s in $server){
# replaced $_ with $s
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$s.Name)
$RegKey= $Reg.OpenSubKey("SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters")
$description = $RegKey.GetValue("srvcomment")
# casting as array is required to use +=
# disclaimer: the array is destroyed and rebuilt on each run.
[array]$myData += New-Object psobject -Property #{
Server = $s.Name
Description = $description
}
}
# If you want a CSV without the top info line, use the notypeinfo switch
# Select-Object gives you the column order you want.
$myData | Select-Object Server,Description | Export-Csv .\out1.csv -NoTypeInformation
Edit - comment answer
It is possible to do it without creating an array and from inside the loop, but an object is required by Export-Csv AFAIK.
ForEach-Object:
$server = Get-ADComputer -Filter {OperatingSystem -Like "*Server*" -and Name -Notlike "*DOM*"}
$server | ForEach-Object {
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$_.Name)
$RegKey = $Reg.OpenSubKey("SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters")
New-object psobject -Property #{Server=$_.Name;Description=$RegKey.GetValue("srvcomment")} | Export-Csv .\out1.csv -NoTypeInformation
}
ForEach:
$server = Get-ADComputer -Filter {OperatingSystem -Like "*Server*" -and Name -Notlike "*DOM*"}
foreach($s in $server){
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$s.Name)
$RegKey= $Reg.OpenSubKey("SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters")
New-object psobject -Property #{Server=$s.Name;Description=$RegKey.GetValue("srvcomment")} | Export-Csv .\out1.csv -NoTypeInformation
}
If you have problems with the values, brackets/subexpressions can help:
New-object psobject -Property #{Server=($_.Name);Description=($RegKey.GetValue("srvcomment"))} | Export-Csv .\out1.csv -NoTypeInformation
New-object psobject -Property #{Server=$($_.Name);Description=$($RegKey.GetValue("srvcomment"))} | Export-Csv .\out1.csv -NoTypeInformation
Your ForEach-Object only contains statements (variable assignments). It's not returning anything. You need to return something, otherwise you're passing an empty pipeline to Export-CSV.
I'm guessing that, instead of setting variables called $Reg, $RegKey, and $description (and then never using their values), what you actually want to do is create columns in the CSV called Reg, RegKey, and description. In that case, you want to yield a [PSCustomObject] (with those properties added to it) each time through the loop.
$server = Get-ADComputer -Filter {OperatingSystem -Like "*Server*" -and Name -Notlike "*DOM*"}
$server | ForEach-Object {
[PSCustomObject] #{
Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$_.Name)
RegKey = $Reg.OpenSubKey("SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters")
description = $RegKey.GetValue("srvcomment")
}
} | Export-Csv .\out1.csv
I have an empty array that's storing all my windows services that start with certain strings such as OPS-AmazonServer,not included in the code I provided is where I parse the service to just say it's application name.
I then have a CSV file with a list of service names labeled under 'Application Name'. It looks like this
ApplicationName,Instance,Priority
AuthBridge,,1
AmazonServer,,1
AmexEC,,1
What I want to do is compare the service stored in the array to the CSV list but I can't seem to figure out how the logic flows.
$services = get-service Centinel* -ComputerName $serverName | select -expand name
$centinelServices = #()
$services = get-service OPS* -ComputerName $serverName | select -expand name
$opsServices = #()
$services = #()
foreach($service in $centinelServices) {
$services += $service
}
foreach($service in $opsServices) {
$services += $service
}
$csvLocation = "\\logserver\Cardinal\OPS\QA\Task\conf\Centinel\app-restart.csv"
$masterList = import-csv $csvLocation
$applications = #()
$masterList | ForEach-Object {$applications += $_.ApplicationName}
forEach($service in $services){
forEach($application in $applications){
if($service -eq $application){
"$service match found"
}
else {
"$service match not found"
}
}
Ok, easiest way to do this is to use Compare-Object, and a little magic with Select.
I'm going to assume that the ApplicationName column in your CSV is a list of strings that match up with the Name property in your Windows Services list. So let's start by importing that CSV, and changing the property name of ApplicationName to just Name, so that it matches the related property on your Windows Service objects.
$masterList = Import-Csv $csvLocation | Select #{l='Name';e={$_.ApplicationName}}
Then we simply use Compare-Object to see what's in both lists:
Compare-Object (Get-Service) -DifferenceObject $masterList -Property Name -IncludeEqual
If you wanted to parse that you can always pipe it to a Where clause, or use combinations of -IncludeEqual and -ExcludeDifferent parameters:
$masterList = Import-Csv $csvLocation | Select #{l='Name';e={$_.ApplicationName}}
$myServices = Get-Service
$foundServices = Compare-Object $myServices -DifferenceObject $masterList -Property Name -IncludeEqual -ExcludeDifferent
$servicesNotInMaster = Compare-Object $myServices -DifferenceObject $masterList -Property Name | Where {$_.SideIndicator -eq '<='}
$servicesNotFoundLocally = Compare-Object $myServices -DifferenceObject $masterList -Property Name | Where {$_.SideIndicator -eq '=>'}
Or using the Switch cmdlet to do it all in one go:
$masterList = Import-Csv $csvLocation | Select #{l='Name';e={$_.ApplicationName}}
$myServices = Get-Service
Switch(Compare-Object $myServices -dif $masterList -prop Name -includeequal -PassThru){
{$_.SideIndicator -eq '<='} {[array]$servicesNotInMaster += $_}
{$_.SideIndicator -eq '=>'} {[array]$servicesNotFoundLocally += $_}
{$_.SideIndicator -eq '=='} {[array]$foundServices += $_}
}
Edit: Ok, updating from your addition to the OP. Looks like you could be well served by simply using a Where clause rather than getting services over and over.
$services = Get-Service -ComputerName $serverName | Where{$_.Name -like 'ops*' -or $_.Name -like 'Centinel*'} | Select -Expand Name
Then you import your CSV, and use Select -Expand again to get the value of the property, rather than looping through it like you were before.
$masterList = Import-Csv $csvLocation | Select -Expand ApplicationName
Now you just have two arrays of strings, so this actually gets even simpler than comparing objects... You can use the -in operator in a Where statement like this:
$services | Where{$_ -in $masterList} | ForEach{"$_ match found"}
That basically filters the $services array to look for any strings that are in the $masterList array. This will only work for exact matches though! So if the service is listed as 'OPS-AmazonServer', but in your CSV file it is listed at just 'AmazonServer' it will not work! I use that example specifically because you have that in your example in your question. You specifically call out the service named 'OPS-AmazonServer' and then in your CSV sample you list just 'AmazonServer'.
If the listings in the CSV are partial strings that you want to match against you could use RegEx to do it. This will probably make less sense if you aren't familiar with RegEx, but this would work:
$services = Get-Service -ComputerName $serverName | Where{$_.Name -like 'ops*' -or $_.Name -like 'Centinel*'} | Select -Expand Name
$masterList = (Import-Csv $csvLocation | ForEach{[regex]::escape($_.ApplicationName)}) -join '|'
$services | Where{ $_ -match $masterList } | ForEach{"$_ match found"}
I need some help writing a script as i am struggling to understand the logic.
I basically have a list of user ids that i need to check to see if they have two certain AD groups. If they have, these need to be outputted into a csv and highlighted.
Can anyone help to get me started? I need to use the Quest Powershell cmdlets
Here is the code
$textFileContents = Get-Content C:\temp\powershell\users.txt
$results = #()
foreach($username in $textFileContents){
$groups = get-qaduser $username |select -expand memberof
if ($groups -match "grpuip1" -and $groups -match "group2"){
echo $group
}
}
check this to begin :
"user1","user2" | foreach {
$groups = get-qaduser $_ |select -expand memberof
if ($groups -match "GROUP1" -and $groups -match "GROUP2"){
echo $_
}
}
I'd use the cmdlet Get-QADMemberOf instead of Get-QADUser. There's nothing wrong with what you're doing, but it's retrieving more information than you need.
Try this to start with:
$textFileContents = Get-Content C:\temp\powershell\users.txt
# Rather than initializing the array, and adding new elements,
# just output each element of the loop to the pipeline, and
# assign the results of the whole pipeline to the variable.
# This is *much* faster than adding to an array
$results = $textFileContents | ForEach-Object {
$userGroups = Get-QADMemberOf $_
if ($userGroups -contains "group1" -and $userGroups -contains "group2") {
New-Object -TypeName PSObject -Property #{"UserName" = $_; "Groups" = ($userGroups -join ",");}
}
}
$results | ConvertTo-Csv -NoTypeInformation | Set-Content C:\Filename.txt