How can I count running EC2 Instances? - powershell

I'm looking for a very basic script to count the number of running EC2 instances at AWS using PowerShell. I have found several methods but for some reason when I try them, I do not get the results I expect.
The closest I have is this:
$instancestate = (get-ec2instance).instances.state.name
$instancestate
which returns:
stopped
running
stopped
stopped
running
(the list goes on for about 80 instances)
I wish to have a response that counts those which are running.

I'm not sure about others, but I prefer to explicitly assign my ec2 filters to variables, and then list them when calling something like Get-EC2Instance. This makes it easier to work with filters if you need to to filter on multiple conditions.
Here's a working example of what you're after, where I have 6 running instances:
# Create the filter
PS C:\> $filterRunning = New-Object Amazon.EC2.Model.Filter -Property #{Name = "instance-state-name"; Value = "running"}
# Force output of Get-EC2Instance into a collection.
PS C:\> $runningInstances = #(Get-EC2Instance -Filter $filterRunning)
# Count the running instances (more literally, count the collection iterates)
PS C:\> $runningInstances.Count
6

Count all instances with separate counters for total, running and stopped ones:
(Get-EC2Instance).Instances | group InstanceType | select Name,
#{n='Total';e={$_.Count }}, #{n='Running';e={($_.Group | ? { $_.state.Name -
eq "running" }).Count }}, #{n='Stopped';e={($_.Group | ? { $_.state.Name -eq
"stopped" }).Count }}

Related

Powershell Parallel Loop Not Recognizing List

I have a powershell script that is trying to get every AD group and their members. Since my real code is running a Get-ADUser on every user in every group, I am using parallel loops to save a good amount of time (side note: after testing I have found that using multiple Get-ADUser commands is typically faster than Get-ADGroupMember). However, I have noticed that I cannot view the members of a group when running a parallel loop. I have written some basic code for testing:
$Groups = Get-ADGroup -Filter * -Properties Created,Modified,Description,Members | select-object -first 50
# Loop A
$Groups | foreach-object {
$psitem.Members
}
# Loop B
$Groups | foreach-object -parallel {
$psitem.Members
}
For the test code above I can verify that $Groups does indeed have the Members property. They gettype() output is below:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False ADPropertyValueCollection System.Collections.CollectionBase
Loop A above prints every group member as expected, however Loop B always returns nothing. Does anybody know why this may be? I would like to use the double parallel loops if possible, just to save a lot of time as this script will be running periodically.
My PS version is 7.2.7
As stated in the comments, using $PSItem['Members'] does the trick. See GitHub Issue #14604 for details.
$Groups = Get-ADGroup -Filter * -Properties Created,Modified,Description,Members | select-object -first 50
$A = $Groups | foreach-object {
$psitem.Members
}
$B = $Groups | foreach-object -parallel {
$PSItem['Members']
}
echo "A: $($A.count) ----- B: $($B.count)"

powershell get mac adress and output as text [duplicate]

Let's say we have an array of objects $objects. Let's say these objects have a "Name" property.
This is what I want to do
$results = #()
$objects | %{ $results += $_.Name }
This works, but can it be done in a better way?
If I do something like:
$results = objects | select Name
$results is an array of objects having a Name property. I want $results to contain an array of Names.
Is there a better way?
I think you might be able to use the ExpandProperty parameter of Select-Object.
For example, to get the list of the current directory and just have the Name property displayed, one would do the following:
ls | select -Property Name
This is still returning DirectoryInfo or FileInfo objects. You can always inspect the type coming through the pipeline by piping to Get-Member (alias gm).
ls | select -Property Name | gm
So, to expand the object to be that of the type of property you're looking at, you can do the following:
ls | select -ExpandProperty Name
In your case, you can just do the following to have a variable be an array of strings, where the strings are the Name property:
$objects = ls | select -ExpandProperty Name
As an even easier solution, you could just use:
$results = $objects.Name
Which should fill $results with an array of all the 'Name' property values of the elements in $objects.
To complement the preexisting, helpful answers with guidance of when to use which approach and a performance comparison.
Outside of a pipeline[1], use (requires PSv3+):
$objects.Name # returns .Name property values from all objects in $objects
as demonstrated in rageandqq's answer, which is both syntactically simpler and much faster.
Accessing a property at the collection level to get its elements' values as an array (if there are 2 or more elements) is called member-access enumeration and is a PSv3+ feature.
Alternatively, in PSv2, use the foreach statement, whose output you can also assign directly to a variable: $results = foreach ($obj in $objects) { $obj.Name }
If collecting all output from a (pipeline) command in memory first is feasible, you can also combine pipelines with member-access enumeration; e.g.:
(Get-ChildItem -File | Where-Object Length -lt 1gb).Name
Tradeoffs:
Both the input collection and output array must fit into memory as a whole.
If the input collection is itself the result of a command (pipeline) (e.g., (Get-ChildItem).Name), that command must first run to completion before the resulting array's elements can be accessed.
In a pipeline, in case you must pass the results to another command, notably if the original input doesn't fit into memory as a whole, use: $objects | Select-Object -ExpandProperty Name
The need for -ExpandProperty is explained in Scott Saad's answer (you need it to get only the property value).
You get the usual pipeline benefits of the pipeline's streaming behavior, i.e. one-by-one object processing, which typically produces output right away and keeps memory use constant (unless you ultimately collect the results in memory anyway).
Tradeoff:
Use of the pipeline is comparatively slow.
For small input collections (arrays), you probably won't notice the difference, and, especially on the command line, sometimes being able to type the command easily is more important.
Here is an easy-to-type alternative, which, however is the slowest approach; it uses ForEach-Object via its built-in alias, %, with simplified syntax (again, PSv3+):
; e.g., the following PSv3+ solution is easy to append to an existing command:
$objects | % Name # short for: $objects | ForEach-Object -Process { $_.Name }
Note: Use of the pipeline is not the primary reason this approach is slow, it is the inefficient implementation of the ForEach-Object (and Where-Object) cmdlets, up to at least PowerShell 7.2. This excellent blog post explains the problem; it led to feature request GitHub issue #10982; the following workaround greatly speeds up the operation (only somewhat slower than a foreach statement, and still faster than .ForEach()):
# Speed-optimized version of the above.
# (Use `&` instead of `.` to run in a child scope)
$objects | . { process { $_.Name } }
The PSv4+ .ForEach() array method, more comprehensively discussed in this article, is yet another, well-performing alternative, but note that it requires collecting all input in memory first, just like member-access enumeration:
# By property name (string):
$objects.ForEach('Name')
# By script block (more flexibility; like ForEach-Object)
$objects.ForEach({ $_.Name })
This approach is similar to member-access enumeration, with the same tradeoffs, except that pipeline logic is not applied; it is marginally slower than member-access enumeration, though still noticeably faster than the pipeline.
For extracting a single property value by name (string argument), this solution is on par with member-access enumeration (though the latter is syntactically simpler).
The script-block variant ({ ... }) allows arbitrary transformations; it is a faster - all-in-memory-at-once - alternative to the pipeline-based ForEach-Object cmdlet (%).
Note: The .ForEach() array method, like its .Where() sibling (the in-memory equivalent of Where-Object), always returns a collection (an instance of [System.Collections.ObjectModel.Collection[psobject]]), even if only one output object is produced.
By contrast, member-access enumeration, Select-Object, ForEach-Object and Where-Object return a single output object as-is, without wrapping it in a collection (array).
Comparing the performance of the various approaches
Here are sample timings for the various approaches, based on an input collection of 10,000 objects, averaged across 10 runs; the absolute numbers aren't important and vary based on many factors, but it should give you a sense of relative performance (the timings come from a single-core Windows 10 VM:
Important
The relative performance varies based on whether the input objects are instances of regular .NET Types (e.g., as output by Get-ChildItem) or [pscustomobject] instances (e.g., as output by Convert-FromCsv).
The reason is that [pscustomobject] properties are dynamically managed by PowerShell, and it can access them more quickly than the regular properties of a (statically defined) regular .NET type. Both scenarios are covered below.
The tests use already-in-memory-in-full collections as input, so as to focus on the pure property extraction performance. With a streaming cmdlet / function call as the input, performance differences will generally be much less pronounced, as the time spent inside that call may account for the majority of the time spent.
For brevity, alias % is used for the ForEach-Object cmdlet.
General conclusions, applicable to both regular .NET type and [pscustomobject] input:
The member-enumeration ($collection.Name) and foreach ($obj in $collection) solutions are by far the fastest, by a factor of 10 or more faster than the fastest pipeline-based solution.
Surprisingly, % Name performs much worse than % { $_.Name } - see this GitHub issue.
PowerShell Core consistently outperforms Windows Powershell here.
Timings with regular .NET types:
PowerShell Core v7.0.0-preview.3
Factor Command Secs (10-run avg.)
------ ------- ------------------
1.00 $objects.Name 0.005
1.06 foreach($o in $objects) { $o.Name } 0.005
6.25 $objects.ForEach('Name') 0.028
10.22 $objects.ForEach({ $_.Name }) 0.046
17.52 $objects | % { $_.Name } 0.079
30.97 $objects | Select-Object -ExpandProperty Name 0.140
32.76 $objects | % Name 0.148
Windows PowerShell v5.1.18362.145
Factor Command Secs (10-run avg.)
------ ------- ------------------
1.00 $objects.Name 0.012
1.32 foreach($o in $objects) { $o.Name } 0.015
9.07 $objects.ForEach({ $_.Name }) 0.105
10.30 $objects.ForEach('Name') 0.119
12.70 $objects | % { $_.Name } 0.147
27.04 $objects | % Name 0.312
29.70 $objects | Select-Object -ExpandProperty Name 0.343
Conclusions:
In PowerShell Core, .ForEach('Name') clearly outperforms .ForEach({ $_.Name }). In Windows PowerShell, curiously, the latter is faster, albeit only marginally so.
Timings with [pscustomobject] instances:
PowerShell Core v7.0.0-preview.3
Factor Command Secs (10-run avg.)
------ ------- ------------------
1.00 $objects.Name 0.006
1.11 foreach($o in $objects) { $o.Name } 0.007
1.52 $objects.ForEach('Name') 0.009
6.11 $objects.ForEach({ $_.Name }) 0.038
9.47 $objects | Select-Object -ExpandProperty Name 0.058
10.29 $objects | % { $_.Name } 0.063
29.77 $objects | % Name 0.184
Windows PowerShell v5.1.18362.145
Factor Command Secs (10-run avg.)
------ ------- ------------------
1.00 $objects.Name 0.008
1.14 foreach($o in $objects) { $o.Name } 0.009
1.76 $objects.ForEach('Name') 0.015
10.36 $objects | Select-Object -ExpandProperty Name 0.085
11.18 $objects.ForEach({ $_.Name }) 0.092
16.79 $objects | % { $_.Name } 0.138
61.14 $objects | % Name 0.503
Conclusions:
Note how with [pscustomobject] input .ForEach('Name') by far outperforms the script-block based variant, .ForEach({ $_.Name }).
Similarly, [pscustomobject] input makes the pipeline-based Select-Object -ExpandProperty Name faster, in Windows PowerShell virtually on par with .ForEach({ $_.Name }), but in PowerShell Core still about 50% slower.
In short: With the odd exception of % Name, with [pscustomobject] the string-based methods of referencing the properties outperform the scriptblock-based ones.
Source code for the tests:
Note:
Download function Time-Command from this Gist to run these tests.
Assuming you have looked at the linked code to ensure that it is safe (which I can personally assure you of, but you should always check), you can install it directly as follows:
irm https://gist.github.com/mklement0/9e1f13978620b09ab2d15da5535d1b27/raw/Time-Command.ps1 | iex
Set $useCustomObjectInput to $true to measure with [pscustomobject] instances instead.
$count = 1e4 # max. input object count == 10,000
$runs = 10 # number of runs to average
# Note: Using [pscustomobject] instances rather than instances of
# regular .NET types changes the performance characteristics.
# Set this to $true to test with [pscustomobject] instances below.
$useCustomObjectInput = $false
# Create sample input objects.
if ($useCustomObjectInput) {
# Use [pscustomobject] instances.
$objects = 1..$count | % { [pscustomobject] #{ Name = "$foobar_$_"; Other1 = 1; Other2 = 2; Other3 = 3; Other4 = 4 } }
} else {
# Use instances of a regular .NET type.
# Note: The actual count of files and folders in your file-system
# may be less than $count
$objects = Get-ChildItem / -Recurse -ErrorAction Ignore | Select-Object -First $count
}
Write-Host "Comparing property-value extraction methods with $($objects.Count) input objects, averaged over $runs runs..."
# An array of script blocks with the various approaches.
$approaches = { $objects | Select-Object -ExpandProperty Name },
{ $objects | % Name },
{ $objects | % { $_.Name } },
{ $objects.ForEach('Name') },
{ $objects.ForEach({ $_.Name }) },
{ $objects.Name },
{ foreach($o in $objects) { $o.Name } }
# Time the approaches and sort them by execution time (fastest first):
Time-Command $approaches -Count $runs | Select Factor, Command, Secs*
[1] Technically, even a command without |, the pipeline operator, uses a pipeline behind the scenes, but for the purpose of this discussion using the pipeline refers only to commands that use |, the pipeline operator, and therefore by definition involve multiple commands.
Caution, member enumeration only works if the collection itself has no member of the same name. So if you had an array of FileInfo objects, you couldn't get an array of file lengths by using
$files.length # evaluates to array length
And before you say "well obviously", consider this. If you had an array of objects with a capacity property then
$objarr.capacity
would work fine UNLESS $objarr were actually not an [Array] but, for example, an [ArrayList]. So before using member enumeration you might have to look inside the black box containing your collection.
(Note to moderators: this should be a comment on rageandqq's answer but I don't yet have enough reputation.)
I learn something new every day! Thank you for this. I was trying to achieve the same. I was directly doing this:
$ListOfGGUIDs = $objects.{Object GUID}
Which basically made my variable an object again! I later realized I needed to define it first as an empty array,
$ListOfGGUIDs = #()

Powershell Where-Object doesn't seem to filter

I'm trying to do some reporting on Azure Policies. I'll eventually be filtering on dates, but having trouble filtering on anything, so present the following sample.
PS C:\>$defstrings = az policy definition list --management-group "mgsandbox" # returns an array of strings
PS C:\>$def = ConvertFrom-Json -InputObject ($defstrings -join "`n") -depth 99 # converts to an array of PSCustomObject
PS C:\>$def.count
2070
PS C:\>$sel = Where-Object -inputobject $def -FilterScript { $_.displayName -eq "Kubernetes cluster containers should not share host process ID or host IPC namespace" }
PS C:\>$sel.count
2070
PS C:\> $def[0].displayName -eq "Kubernetes cluster containers should not share host process ID or host IPC namespace"
False
While I might possibly find more than one hit on the displayName, there are clearly a non-zero set of displayNames that do not match the filter, yet the selection is getting all of them.
Any suggestions what's wrong with my syntax? It seems straightforward.
Do not use an -InputObject argument with Where-Object; instead, provide input via the pipeline:
# Use the pipeline to provide input, don't use -InputObject
$def | Where-Object -FilterScript { $_.displayName -eq "Kubernetes cluster containers should not share host process ID or host IPC namespace" }
In most cmdlets, the -InputObject parameter is a mere implementation detail whose purpose is to facilitate pipeline input and cannot be meaningfully used directly; see this answer for more information.
As for what you tried:
When you use -InputObject, an argument that is a collection (enumerable) is passed as a whole to the cmdlet, whereas using the same in the pipeline cause its enumeration, i.e. the collection's elements are passed, one by one.
A simplified example:
# Sample array.
$arr = 1, 2, 3
# WRONG: array is passed *as a whole*
# and in this case outputs *all* its elements.
# -> 1, 2, 3
Where-Object -InputObject $arr { $_ -eq 2 }
That is, the script block passed to Where-Object is executed once, with the automatic $_ variable bound to the array as a whole, so that the above is in effect equivalent to:
if ($arr -eq 2) { $arr }
Since $arr -eq 2 evaluates to $true in a Boolean context (the if conditional), $arr as a whole is output (although on output it is enumerated), giving the impression that no filtering took place.
The reason that $arr -eq 2 evaluates to $true is that the -eq operator, among others, supports arrays as its LHS, in which case the behavior changes to filtering, by returning the sub-array of matching elements instead of a Boolean, so that 1, 2, 3 -eq 2 yields #(2) (an array containing the one matching element, 2), and coercing #(2) to a Boolean yields $true ([bool] #(2)).[1]
Conversely, if the implied conditional yields $false (e.g., $_ -eq 5), no output is produced at all.
By contrast, if you use the pipeline, you'll get the desired behavior:
# Sample array.
$arr = 1, 2, 3
# OK: Array elements are enumerated, i.e.
# sent *one by one* through the pipeline.
# -> 2
$arr | Where-Object{ $_ -eq 2 }
Alternatively, you can bypass the pipeline by using the intrinsic .Where() method:
Note: This requires collecting all input in memory first; however, especially with data already in memory, this approach performs better than the pipeline approach:
# OK:
# -> 2 (wrapped in a collection)
#(1, 2, 3).Where({ $_ -eq 2 })
Note: .Where() always outputs an array-like collection, even when only a single object matches the filter. In practice, however, that usually doesn't matter.
[1] For a summary of PowerShell's to-Boolean coercion rules, see the bottom section of this answer.

Merge two PSCustomObjects into one- PowerShell

I need help in PowerShell to combine two outputs or two PSCustomObjects into One.
For example,
$services = Get-Service | Select Name, Starttype,Status
$processes = Get-Process | Select ID
I need the output with the table headers
Name, Starttype, Status, ID
I have already tried creating CSV and joining them but the problem is Process ID starts when the entire output ends for the services. I need them to a parallel.
Second I have tried to create PSCustomObjects but no luck.
Please help me with the PowerShell code.
Actual code that I'm trying to achieve.
**$exclusionItems = #()
$OasHighItems = #()
foreach($item in $items){
$exclusionItems += [PSCustomObject]#{
EXCLUSION_BY_NAME_OR_LOCATION = $item.EXCLUSION_BY_NAME_OR_LOCATION
EXCLUSION_EXCLUDE_SUBFOLDERS = $item.EXCLUSION_EXCLUDE_SUBFOLDERS
EXCLUSION_ON_READ= $item.EXCLUSION_ON_READ
}
}
foreach($oas in $oashigh){
$oashighItems += [PSCustomObject]#{
OAS_PROCESSES_LIST = $oas
}
}
$Array = #()
$Array = $exclusionItems,$oashighItems
$Array | Update-FirstObjectProperties | Export-Excel $ExcelParams -TableName Table -Show**
I'm assuming you want to join the two objects by their names, i.e. match the Process-Name with the Service-Name. For this you can loop over all processes & services, keep only those where service-name equals process-name, and use a calculated property to merge the result into one object:
$services = Get-Service;
Get-Process | ForEach-Object {$p = $_; $services |
Where-Object{$p.ProcessName -eq $_.Name} |
Select-Object Name,StartType,Status,#{n='ID';e={$p.ID}}}
The output on my machine is:
Name StartType Status ID
---- --------- ------ --
CcmExec Automatic Running 14856
CmRcService Automatic Running 5748
FusionInventory-Agent Automatic Running 5996
IBMPMSVC Automatic Running 3540
IntelAudioService Automatic Running 6104
... and so on ...

observablecollection predicate filter

I would like to know how to filter the collection $view using a predicate filter. I have seen numerous examples of a predicate filter in C# but not many for PowerShell. It would be great to see a working example in PowerShell.
Basically when I set the filter variable to a string and refresh $view, the collection should filter to show me just the rows or objects that matched the filter. If the filter is empty the entire collection of objects should be shown.
I think this should work in the console without using any forms but i haven't been able to create a filter of the type (system.predicate) in PowerShell.
$a = New-Object System.Collections.ObjectModel.ObservableCollection[object]
$svcs = gsv -ComputerName LocalHost |
select #{n="Server";e={$_.machinename}},Name,Displayname,status
$svcs | ForEach {
$a.Add((
New-Object PSObject -Property #{
Server = $_.server
Name = $_.name
Displayname = $_.displayname
Status = $_.status
}
))
}
$view = [System.Windows.Data.CollectionViewSource]::GetDefaultView($a)
$filter = "bits"
$view.Filter = "Predicate FIlter???"
$view.Refresh()
Just pass a script block which takes a single parameter. The script block should return true for items which you want to include in the view and false for those that you do not want to include. The following works for me on PowerShell v4:
$view = [System.Windows.Data.CollectionViewSource]::GetDefaultView($a)
Write-Host "Setting filter to 'vss'"
$filter = "vss"
$view.Filter = {param ($item) $item -match $filter}
$view.Refresh()
$view
Write-Host "Setting filter to 'BITS'"
$filter = "BITS"
$view.Refresh()
$view
EDIT: Adding the output printed on a test computer of mine
Running the above script on a test computer resulted in the following output:
Setting filter to 'vss'
Status Server Name Displayname
------ ------ ---- -----------
Running LocalHost SQLWriter SQL Server VSS Writer
Stopped LocalHost vmicvss Hyper-V Volume Shado...
Stopped LocalHost VSS Volume Shadow Copy
Setting filter to 'BITS'
Running LocalHost BITS Background Intellige...