How to Add Operations in Powershell Command - powershell

I am using the following Powershell command in order to extract the name, the assigned RAM and RAM usage of each VMs in the server.
Get-VM | ft Name, Memorydemand, Memoryassigned
However the result of the memorydemand and memoryassigned are in Bytes but I want them to be in Megabytes. Is there a way for me to divide the results of the memorydemand and memoryassigned by 1048576 so that I can get their corresponding MB?
Also, is it also possible to get the average RAM Usage of a certain VM for the last one or two months? Even though Hyper-V is assigning dynamic memory, I just want to double-check.

There's a couple different approaches that I can think of to achieve this.
Use Select-Object to create calculated properties
Use the Select-Object command to create custom, calculated properties.
Get-VM | Select-Object -Property `
Name,
#{ Name = 'MemoryDemandMB'; Expression = { $PSItem.MemoryDemand/1MB } },
#{ Name = 'MemoryAssignedMB'; Expression = { $PSItem.MemoryAssigned/1MB } } |
Format-Table -Property Name, MemorydemandMB, MemoryassignedMB -AutoSize
Use Add-Member to augment the objects
You can use the Add-Member command to add two new properties to the objects. This actually augments the objects, rather than simply appending the properties for the lifetime of the pipeline.
Get-VM |
Add-Member -MemberType ScriptProperty -Name MemoryDemandMB -Value { $this.MemoryDemand/1MB } -PassThru |
Add-Member -MemberType ScriptProperty -Name MemoryAssignedMB -Value { $this.MemoryAssigned/1MB } -PassThru |
Format-Table -Property Name, MemorydemandMB, MemoryassignedMB -AutoSize
Results
Here's what the output looks like on my system.
Name MemoryDemandMB MemoryAssignedMB
---- -------------- ----------------
agent01 0 0
agent02 0 0
dc01 878 1058
denver01 0 0
london01 877 1070
MobyLinuxVM 0 0
munich01 1228 1638
sccm01 2213 2604
swarm01 0 0
UbuntuDesktop 0 0

Related

Creation and modification of Objects in Powershell

I'm a newbie of powershell, I'm starting right now to look at objects, etc.
I'm creating an object in this way:
$myObject = [PSCustomObject]#{
ComputerName = "abc"
Data = "xxx"
}
If I then print $myObject what i get is:
ComputerName Data
------------ ----
abc xxx
And everything is ok, now I want to add a property to that object, and I saw (tell me if i'm wrong) that I can do it in 2 ways: with add-member and with select-object
For example with add-member what I did was:
$myObject | Add-member -NotePropertyName Level -NotePropertyValue Highest
Instead with Select-object I did:
$myobject = 2 (cause i want to add 2 properties, is it right?) | Select-Object -Property Level, Privilege
$myobject.Level = "High"
$myobject.Privilege = "Elevated"
Now if I run $myobject I still only get:
ComputerName Data
------------ ----
abc xxx
What should I do to see all the data, even the one that I added later?
Can I directly add the values to the properties added through Select-Object?
Thanks
You can use the Add-Member method on a PsCustomObject.
$myObject = [PSCustomObject]#{
ComputerName = "abc"
Data = "xxx"
}
$myObject | Add-Member -NotePropertyName Level -NotePropertyValue High
$myObject | Add-Member Privilege Elevated
$myObject
#Output looks like,
ComputerName Data Level Privilege
------------ ---- ----- ---------
abc xxx High Elevated
Update
Not sure at the moment why but will elaborate on it ...
If you print the Pscustomobject and then run the add-member, they seem to be ignored. If you create a hashtable, update it and then convert to PsObject, it works. Following is an example of that hashtable
$myObject = #{
ComputerName = "abc"
Data = "xxx"
}
$myObject | ft
$myObject.Add("Level", "high")
$myObject.Add("Privilege", "Elevated")
[pscustomobject] $myObject | ft
What I found
When you print $myObject then add the data, the data is added but not displayed. This is due to some internal mechanism, unknown to me, that continues to use the same headers from previous command.
If you notice, the data is printed twice under the same heading. If you want to see the differences before and after, pipe it to format-list or format-table to use a different output stream each time.
$myObject = [PSCustomObject]#{
ComputerName = "abc"
Data = "xxx"
}
$myObject | ft
$myObject | Add-Member -NotePropertyName Level -NotePropertyValue High -Force
$myObject | Add-Member Privilege Elevated
$myObject | ft
You can use either Add-Member or Select-Object. Withstanding the advantages or disadvantages in different situations I just want to throw in the Select-Object example. Just so you have both methods:
This example will echo the object with the selected properties:
$myObject |
Select-Object *,
#{Name = 'Level'; Expression = { 'High' } },
#{Name = 'Privilege'; Expression = { 'Elevated' } }
If you want to save the new properties back to the object variable you'll have to reassign like below:
$myObject = $myObject |
Select-Object *,
#{Name = 'Level'; Expression = { 'High' } },
#{Name = 'Privilege'; Expression = { 'Elevated' } }
PowerShell allows you to provide a hash table to define the new properties. You'll note it looks fairly similar to the hash you used to create the object. Typically the expression would leverage the $_ syntax to calculate the property's value. You will often here these referred to as calculated properties.

Compare objects and export the difference

I would like to make a script that compares O365 tenant settings. Reading them is fine, now I would like to make some kind of difference object.
A related question is here, but no answer.
Powershell Compare-Object and getting the Differences into a File
I already have a json file from both tenants created like:
$srcTenant | Select-Object -Property * | ConvertTo-Json | Out-File "$targetfolder\$targetfile"
Now, I would like a file that only contains the properties that are collected with the script below:
I am so far:
$properties = ($srcTenant | Get-Member -MemberType Property | Select-Object -ExpandProperty Name)
$selectedproperties = #{}
$i = 0
foreach ($property in $properties) {
if (Compare-Object $srcTenant $trgTenant -Property "$property") {
$selectedproperties.Add($i, "$property")
$i++
}
}
The $selectedproperties variable contains 9 properties and I would like to export only this 9 in the same format as the other two.
Name Value
---- -----
8 StorageQuotaAllocated
7 StorageQuota
6 ResourceQuotaAllocated
5 ResourceQuota
4 OwnerAnonymousNotification
3 OneDriveStorageQuota
2 DefaultLinkPermission
1 ConditionalAccessPolicy
0 AllowDownloadingNonWebViewableFiles
So, I am looking for something like:
$srcTenant | Select-Object -Property (that 9 property above) | ConvertTo-Json | Out-File "$targetfolder\$targetfile
Other options achieving the same result are welcome too :)
Select-Object -Property can take an array of property names.
see the first example here

In Powershell, how to make an array with custom objects of different sizes/types?

im writing a simple function in order to list subfolders of a particular server share. My problem is that i want all informations in the same array of custom objects. Here is what i did:
function Get-tDollar {
param(
[string]$srv
)
$share = Get-WmiObject -ComputerName $srv -Class Win32_Share -Filter "Name='share`$'" | Select __SERVER,#{n="nPath";e={"\\" + $_.__SERVER + ($_.Path -replace "C:")}}
$inc = 0
Get-ChildItem $share.nPath | % {
$inc++
Add-Member -InputObject $share -MemberType NoteProperty -Name "folder_$inc" -Value $_.Name
}
$share
}
And i use this function like that: #("srv1","srv2") | % { Get-TDollar -srv $_ }
All works fine when the number of subfolders is the same, but when its different, the array contains only the number of folders listed in the first share. For example, for my two first servers, i have this output:
__SERVER nPath folder_1
-------- ----- ------------
srv1 \\srv1\share scripts
srv2 \\srv2\share scripts
But, because srv2 has more folders than the first server, i want this output:
__SERVER nPath repertoire_1 repertoire_2
-------- ----- ------------ ------------
srv1 \\srv1\share scripts
srv2 \\srv2\share scripts config
I know i can first calculate which server has the higher number of folders and then place it in fisrt position, but it seems there are enough lines for something like that. Is there a more efficient/elegant way to do that?
You're getting bitten by the default formatting gremlin:
http://blogs.msdn.com/b/powershell/archive/2006/04/30/586973.aspx
Get-tDollar |
foreach { $_ | format-table }

How do I add a column of incrementing values to cmdlet output?

Suppose I call Get-Service and want to assign a new column ID with the cmdlet output that prints incrementing integers so that:
ID Status Name DisplayName
-- ------ ---- -----------
0 Running AdobeARMservice Adobe Acrobat Update Service
1 Stopped AeLookupSvc Application Experience
2 Stopped ALG Application Layer Gateway Service
I'm trying to use Select-Object right now to add this column, but I don't quite understand how to iterate a variable in this sort of expression. Here's what I've got:
Get-Service |
Select-Object #{ Name = "ID" ; Expression= { } }, Status, Name, DisplayName |
Format-Table -Autosize
Is there a way to iterate integers within Expression= { }, or am I going about this problem the wrong way?
You can do it this way, though you will need to maintain some counter variable outside of the main expression.
$counter = 0
Get-Service |
Select-Object #{ Name = "ID" ; Expression= {$global:counter; $global:counter++} }, Status, Name, DisplayName |
Format-Table -Autosize
Another option, which is perhaps cleaner
Get-Service `
|% {$counter = -1} {$counter++; $_ | Add-Member -Name ID -Value $counter -MemberType NoteProperty -PassThru} `
| Format-Table ID
I asked the same question a different way and got the following answer
$x = 10
Get-Service |
Select-Object #{ Name = "ID" ; Expression={ (([ref]$x).Value++) }}, Status, Name, DisplayName | Format-Table -Autosize
It wasn't at all clear to me that the expression is being invoked within Select-Object's scope, not the pipe's. The [ref] qualifier bumps the increment's result up to the pipe's scope achieving the same result as explicitly specifying the variable as global.

How do you export objects with a varying amount of properties?

Warning - I've asked a similar question in the past but this is slightly different.
tl;dr; I want to export objects which have a varying number of properties. eg; object 1 may have 3 IP address and 2 NICs but object 2 has 7 IP addresses and 4 NICs (but not limited to this amount - it could be N properties).
I can happily capture and build objects that contain all the information I require. If I simply output my array to the console each object is shown with all its properties. If I want to out-file or export-csv I start hitting a problem surrounding the headings.
Previously JPBlanc recommended sorting the objects based on the amount of properties - ie, the object with the most properties would come first and hence the headings for the most amount of properties would be output.
Say I have built an object of servers which has varying properties based on IP addresses and NIC cards. For example;
ServerName: Mordor
IP1: 10.0.0.1
IP2: 10.0.0.2
NIC1: VMXNET
NIC2: Broadcom
ServerName: Rivendell
IP1: 10.1.1.1
IP2: 10.1.1.2
IP3: 10.1.1.3
IP4: 10.1.1.4
NIC1: VMXNET
Initially, if you were to export-csv an array of these objects the headers would be built upon the first object (aka, you would only get ServerName, IP1, IP2, NIC1 and NIC2) meaning for the second object you would lose any subsequent IPs (eg IP3 and IP4). To correct this, before an export I sort based on the number of IP properties - tada - the first object now has the most IPs in the array and hence none of the subsequent objects IPs are lost.
The downside is when you then have a second varying property - eg NICs. Once my sort is complete based on IP we then have the headings ServerName, IP1 - IP4 and NIC1. This means the subsequent object property of NIC2 is lost.
Is there a scalable way to ensure that you aren't losing data when exporting objects like this?
Try:
$o1 = New-Object psobject -Property #{
ServerName="Mordor"
IP1="10.0.0.1"
IP2="10.0.0.2"
NIC1="VMXNET"
NIC2="Broadcom"
}
$o2 = New-Object psobject -Property #{
ServerName="Rivendell"
IP1="10.1.1.1"
IP2="10.1.1.2"
IP3="10.1.1.3"
IP4="10.1.1.4"
NIC1="VMXNET"
}
$arr = #()
$arr += $o1
$arr += $o2
#Creating output
$prop = $arr | % { Get-Member -InputObject $_ -MemberType NoteProperty | Select -ExpandProperty Name } | Select -Unique | Sort-Object
$headers = #("ServerName")
$headers += $prop -notlike "ServerName"
$arr | ft -Property $headers
Output:
ServerName IP1 IP2 IP3 IP4 NIC1 NIC2
---------- --- --- --- --- ---- ----
Mordor 10.0.0.1 10.0.0.2 VMXNET Broadcom
Rivendell 10.1.1.1 10.1.1.2 10.1.1.3 10.1.1.4 VMXNET
If you know the types(NICS, IPS..), but not the count(ex. how many NICS) you could try:
#Creating output
$headers = $arr | % { Get-Member -InputObject $_ -MemberType NoteProperty | Select -ExpandProperty Name } | Select -Unique
$ipcount = ($headers -like "IP*").Count
$niccount = ($headers -like "NIC*").Count
$format = #("ServerName")
for ($i = 1; $i -le $ipcount; $i++) { $format += "IP$i" }
for ($i = 1; $i -le $niccount; $i++) { $format += "NIC$i" }
$arr | ft -Property $format
What about getting a list of all unique property headers and then doing a select on all the objects? When you do a select on an object for a nonexistent property it will create a blank one.
$allHeaders = $arrayOfObjects | % { Get-Member -inputobject $_ -membertype noteproperty | Select -expand Name } | Select -unique
$arrayOfObjects | Select $allHeaders
Granted you are looping through ever object to get the headers, so for a very large amount of objects it may take awhile.
Here's my attempt at a solution. I'm very tired now so hopefully it makes sense. Basically I'm calculating the largest amount of NIC and IP note properties, creating a place holder object that has those amounts of properties, adding it as the first item in a CSV, and then removing it from the CSV.
# Create example objects
$o1 = New-Object psobject -Property #{
ServerName="Mordor"
IP1="10.0.0.1"
IP2="10.0.0.2"
NIC1="VMXNET"
NIC2="Broadcom"
}
$o2 = New-Object psobject -Property #{
ServerName="Rivendell"
IP1="10.1.1.1"
IP2="10.1.1.2"
IP3="10.1.1.3"
IP4="10.1.1.4"
NIC1="VMXNET"
}
# Add to an array
$servers = #($o1, $o2)
# Calculate how many IP and NIC properties there are
$IPColSize = ($servers | Select IP* | %{($_ | gm -MemberType NoteProperty).Count} | Sort-Object -Descending)[0]
$NICColSize = ($servers | Select NIC* | %{($_ | gm -MemberType NoteProperty).Count} | Sort-Object -Descending)[0]
# Build a place holder object that will contain enough properties to cover all of the objects in the array.
$cmd = '$placeholder = "" | Select ServerName, {0}, {1}' -f (#(1..$IPColSize | %{"IP$_"}) -join ", "), (#(1..$NICColSize | %{"NIC$_"}) -join ", ")
Invoke-Expression $cmd
# Convert to CSV and remove the placeholder
$csv = $placeholder,$servers | %{$_ | Select *} | ConvertTo-Csv -NoTypeInformation
$csv | Select -First 1 -Last ($csv.Count-2) | ConvertFrom-Csv | Export-Csv Solution.csv -NoTypeInformation