Powershell Calculated Property - Calling HashTable enumerator? - powershell

I'm unable to display the calculated property column called Logon Type which is translated from a hash table.
The script below is working fine, but I just need to translate the raw value number into a more meaningful description.
function Get-LogonEvents {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('ServerName', 'Server', 'Name')]
[string[]]$ComputerName,
[Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
[PSCredential]$Credential,
[Parameter()]
[ValidateSet("Service", "Interactive", "RemoteInteractive", "NetworkCleartext", "CachedInteractive", "Unlock", "NewCredentials", "Network", "*")]
[string[]]$LogonType = #("Interactive", "RemoteInteractive", "CachedInteractive"),
[string]$UserName,
[Parameter()]
[switch]$Oldest,
[Parameter()]
[int64]$MaxEvents,
[Parameter()]
[datetime]$StartTime = (Get-Date 1/1/1900),
[Parameter()]
[datetime]$StopTime = (Get-Date 1/1/2100)
)
Begin {
Function ParseEventMessage {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline = $true)]
$obj
)
Begin {
$defaultDisplaySet = 'TimeCreated', 'MachineName', 'TargetDomainName', 'TargetUserName'
$defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet(‘DefaultDisplayPropertySet’, [string[]]$defaultDisplaySet)
$PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]#($defaultDisplayPropertySet)
$myHash = #{ }
}
Process {
([xml]($obj.ToXml())).event.eventdata.data | ForEach-Object { $myHash[$PSItem.name] = $PSItem.'#text' }
New-Object -TypeName PSObject -Property $myHash | ForEach-Object {
$PSItem.PSObject.TypeNames.Insert(0, "EventLogRecord.XMLParse")
$PSItem | Add-Member MemberSet PSStandardMembers $PSStandardMembers -PassThru |
Add-Member -MemberType NoteProperty -Name TimeCreated -Value $obj.timecreated -PassThru |
Add-Member -MemberType NoteProperty -Name MachineName -Value $obj.MachineName -PassThru
}
}
}
$hashLogonType = #{
"Interactive" = "2"
"Network" = "3"
"Service" = "5"
"Unlock" = "7"
"NetworkCleartext" = "8"
"NewCredentials" = "9"
"RemoteInteractive" = "10"
"CachedInteractive" = "11"
}
$filter = #"
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">
*[System[
(EventID='4624')
and TimeCreated[#SystemTime>='{0}' and #SystemTime<='{1}']
]
and EventData[
Data[#Name='LogonType'] and ({2})
{3}
]
]
</Select>
</Query>
</QueryList>
"#
}
Process {
foreach ($obj in $ComputerName) {
if ($UserName) {
$joinUserName = "and Data[#Name='TargetuserName'] and (Data='{0}')" -f $UserName
}
$joinLogonType = ($LogonType | ForEach-Object { $hashLogonType[$PSItem] }) -replace '^', "Data='" -replace '$', "'" -join " or "
$objFilter = $filter -f (Get-Date $StartTime -Format s), (Get-Date $StopTime -Format s), $joinLogonType, $joinUserName
$hashEventParm = #{
ComputerName = $obj
FilterXml = $objFilter
}
if ($Credential) { $hashEventParm['Credential'] = $Credential }
if ($MaxEvents) { $hashEventParm['MaxEvents'] = $MaxEvents }
$objFilter | Write-Verbose
Get-WinEvent #hashEventParm | ParseEventMessage
}
}
End { }
}
$TargetDomainNameException = #('Window Manager','Font Driver Host')
$exceptionRegex = $TargetDomainNameException -join "|"
Get-LogonEvents -ComputerName 'Localhost' -MaxEvents 10 |
Where-Object { ($_.TargetDomainName -notmatch $exceptionRegex) } |
Select-Object WorkstationName,
TargetUserName,
TargetDomainName,
Type,
LogonType,
#{n ='LogonType'; e={$hashLogonType[[string]$_.LogonType]}},
#{n = 'Logon Type'; e = {$hashLogonType["$($_.LogonType)"]}},
ProcessName,
IPAddress,
#{n="Host Name"; e={([System.Net.Dns]::GetHostByAddress($_.IPaddress).Hostname)}},
TimeCreated |
Out-GridView
Error:
I have modifiedthe Calculated property like:
#{n = 'Logon Type'; e = {$hashLogonType["$($_.LogonType)"]}},
Somehow it is still not displaying the column "Logon Type", however, the raw value on LogonType column still showing as 10, 3 ,etc...?

I see two problems.
$hashLogonType is defined inside the function and won't be available in the global scope.
The keys for $hashLogonType are by [string] not by [int].
If you're able to modify the original function, you might consider adding a property where the string value of LogonType is saved.
Otherwise, keep a copy of $hashLogonType in your variable scope with integers as keys, and base your calculated property on that.
The easiest way to get what you want is to create your own hash table and use it in your pipeline.
# Create a hash table for your own use in your variable scope.
$myHashTable = #{
2 = "Interactive"
3 = "Network"
5 = "Service"
7 = "Unlock"
8 = "NetworkCleartext"
9 = "NewCredentials"
10 = "RemoteInteractive"
11 = "CachedInteractive"
}
# Shim object.
$exampleObject = [PSCustomObject]#{
LogonType = 2
WorkstationName = "myHost.example.com"
}
# Modify your pipeline to use your hash table.
$exampleObject |
Select-Object -Property WorkstationName, LogonType, #{label="Logon Title";expression={$myHashTable[$_.LogonType]}}
PS> ./Answer 02.ps1
WorkstationName LogonType Logon Title
--------------- --------- -----------
myHost.example.com 2 Interactive
In principle, it is possible to modify the original function. But, I don't have any data to test with. Maybe Doug can help. He seems to have access to an event log.
You would have to do two things.
Add a hash table with integer keys in scope for ParseEventMessage(). For example, add the hash table to ParseEventMessage()'s Begin block.
Where it says
Add-Member -MemberType NoteProperty -Name MachineName -Value $obj.MachineName -PassThru
Add another property by extending that pipeline:
Add-Member -MemberType NoteProperty -Name LogonTitle -Value {$myHashTable[$_.LogonType]} -PassThru

Edit: Yes Mike is absolutely correct, the hashtable was defined inside the get-logonevents function and not used. I've moved it out and now it should work.
I think you should reverse the assignment of the hashtable. Either as an int or a string should work then. I did it like this and it worked fine.
function Get-LogonEvents {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('ServerName', 'Server', 'Name')]
[string[]]$ComputerName,
[Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
[PSCredential]$Credential,
[Parameter()]
[ValidateSet("Service", "Interactive", "RemoteInteractive", "NetworkCleartext", "CachedInteractive", "Unlock", "NewCredentials", "Network", "*")]
[string[]]$LogonType = #("Interactive", "RemoteInteractive", "CachedInteractive"),
[string]$UserName,
[Parameter()]
[switch]$Oldest,
[Parameter()]
[int64]$MaxEvents,
[Parameter()]
[datetime]$StartTime = (Get-Date 1/1/1900),
[Parameter()]
[datetime]$StopTime = (Get-Date 1/1/2100)
)
Begin {
Function ParseEventMessage {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline = $true)]
$obj
)
Begin {
$defaultDisplaySet = 'TimeCreated', 'MachineName', 'TargetDomainName', 'TargetUserName'
$defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet(‘DefaultDisplayPropertySet’, [string[]]$defaultDisplaySet)
$PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]#($defaultDisplayPropertySet)
$myHash = #{ }
}
Process {
([xml]($obj.ToXml())).event.eventdata.data | ForEach-Object { $myHash[$PSItem.name] = $PSItem.'#text' }
New-Object -TypeName PSObject -Property $myHash | ForEach-Object {
$PSItem.PSObject.TypeNames.Insert(0, "EventLogRecord.XMLParse")
$PSItem | Add-Member MemberSet PSStandardMembers $PSStandardMembers -PassThru |
Add-Member -MemberType NoteProperty -Name TimeCreated -Value $obj.timecreated -PassThru |
Add-Member -MemberType NoteProperty -Name MachineName -Value $obj.MachineName -PassThru
}
}
}
$filter = #"
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">
*[System[
(EventID='4624')
and TimeCreated[#SystemTime>='{0}' and #SystemTime<='{1}']
]
and EventData[
Data[#Name='LogonType'] and ({2})
{3}
]
]
</Select>
</Query>
</QueryList>
"#
}
Process {
foreach ($obj in $ComputerName) {
if ($UserName) {
$joinUserName = "and Data[#Name='TargetuserName'] and (Data='{0}')" -f $UserName
}
$joinLogonType = ($LogonType | ForEach-Object { $hashLogonType[$PSItem] }) -replace '^', "Data='" -replace '$', "'" -join " or "
$objFilter = $filter -f (Get-Date $StartTime -Format s), (Get-Date $StopTime -Format s), $joinLogonType, $joinUserName
$hashEventParm = #{
ComputerName = $obj
FilterXml = $objFilter
}
if ($Credential) { $hashEventParm['Credential'] = $Credential }
if ($MaxEvents) { $hashEventParm['MaxEvents'] = $MaxEvents }
$objFilter | Write-Verbose
Get-WinEvent #hashEventParm | ParseEventMessage
}
}
End { }
}
$hashLogonType = #{
2 = "Interactive"
3 = "Network"
5 = "Service"
7 = "Unlock"
8 = "NetworkCleartext"
9 = "NewCredentials"
10 = "RemoteInteractive"
11 = "CachedInteractive"
}
$TargetDomainNameException = #('Window Manager','Font Driver Host')
$exceptionRegex = $TargetDomainNameException -join "|"
Get-LogonEvents -ComputerName 'Localhost' -MaxEvents 10 -OutVariable LogonEvents |
Where-Object { ($_.TargetDomainName -notmatch $exceptionRegex) } |
Select-Object WorkstationName,
TargetUserName,
TargetDomainName,
Type,
#{n="LogonType";e={$hashLogonType.[int]$_.logontype}},
ProcessName,
IPAddress,
#{n="Host Name"; e={([System.Net.Dns]::GetHostByAddress($_.IPaddress).Hostname)}},
TimeCreated |
Out-GridView

Related

Can't pass the value to the function

I am trying to make a condition that if the value in the column is equal to the new column that I will be making is post the value from my else. But my function can't get the values from my data. Any advice?
function MainGroupChoice{
param (
[Parameter(ValuefromPipeline = $true)][String] $value1,
[Parameter(ValuefromPipeline = $true)][String] $value2,
[Parameter(ValuefromPipeline = $true)][String] $choice1,
[Parameter(ValuefromPipeline = $true)][String] $choice2
)
if ($value1 -eq $value2) {
return
Add-Member -NotePropertyName MainGroup -NotePropertyValue $choice1 -PassThru -Force
}
else {
return
Add-Member -NotePropertyName MainGroup -NotePropertyValue $choice2 -PassThru -Force
}
}
$table2 = $NestedGroupUsers | MainGroupChoice -choice1 $ADgroupname.name -choice2 $groupNestedName -value1 $table2.ParentGroup -value2 $nestedmember.distinguishedName
I got this error
The input object cannot
| be bound to any
| parameters for the
| command either because
| the command does not
| take pipeline input or
| the input and its
| properties do not match
| any of the parameters
| that take pipeline
| input.
I've answered your question below, but I feel obligated to tell you that this one-liner can likely accomplish the same as all the code below:
$nestedGroups |Select Value*,#{Name='MainGroup';Expression={if($_.value1 -eq $_.value2){$_.choice1}else{$_.choice2}}}
It looks like you'll want to:
Declare a parameter to receive the whole input object (so you can modify and/or return it later), and then
Mark the remaining parameters ValuefromPipelineByPropertyName (instead of ValuefromPipeline), and finally
Move the code into an explicit process {} block - this will ensure it's executed once for each input object bound via the pipeline
function MainGroupChoice {
param (
[Parameter(ValueFromPipeline)]$InputObject,
[Parameter(ValuefromPipelineByPropertyName = $true)][String] $Value1,
[Parameter(ValuefromPipelineByPropertyName = $true)][String] $Value2,
[Parameter(ValuefromPipelineByPropertyName = $true)][String] $Choice1,
[Parameter(ValuefromPipelineByPropertyName = $true)][String] $Choice2
)
process {
if ($value1 -eq $value2) {
return $InputObject |Add-Member -NotePropertyName MainGroup -NotePropertyValue $choice1 -PassThru -Force
}
else {
return $InputObject |Add-Member -NotePropertyName MainGroup -NotePropertyValue $choice2 -PassThru -Force
}
}
}
Testing with some mock input:
#(
[pscustomobject]#{
Value1 = 123
Value2 = 123
Choice1 = "Pick me!"
Choice2 = "Not me..."
}
[pscustomobject]#{
Value1 = 123
Value2 = 456
Choice1 = "Not me..."
Choice2 = "Pick me!"
}
) |MainGroupChoice |Select Value*,MainGroup
... and we should expect output like:
Value1 Value2 MainGroup
------ ------ ---------
123 123 Pick me!
123 456 Pick me!

PowerShell - How to tell if two objects are identical

Let's say you have two objects that are identical (meaning they have the same properties and the same values respectively).
How do you test for equality?
Example
$obj1 & $obj2 are identical
Here's what I've tried:
if($obj1 -eq $obj2)
{
echo 'true'
} else {
echo 'false'
}
# RETURNS "false"
if(Compare-Object -ReferenceObject $obj1 -DifferenceObject $obj2)
{
echo 'true'
} else {
echo 'false'
}
# RETURNS "false"
Edit
This is not identical
You can compare two PSObject objects for equality of properties and values by using Compare-Object to compare the Properties properties of both PSObjectobjects. Example:
if ( -not (Compare-Object $obj1.PSObject.Properties $obj2.PSObject.Properties) ) {
"object properties and values match"
}
else {
"object properties and values do not match"
}
If you want it in a function:
function Test-PSCustomObjectEquality {
param(
[Parameter(Mandatory = $true)]
[PSCustomObject] $firstObject,
[Parameter(Mandatory = $true)]
[PSCustomObject] $secondObject
)
-not (Compare-Object $firstObject.PSObject.Properties $secondObject.PSObject.Properties)
}
I'd suggest using Compare-Object for this task:
Function Test-Objects
{
Param(
[Parameter(Mandatory,Position=0)]
[PSCustomObject]$Obj1,
[Parameter(Mandatory,Position=1)]
[PSCustomObject]$Obj2
)
[Void](Compare-Object -ReferenceObject $Obj1.PSObject.Properties -DifferenceObject.PSObject.Properties $Obj2 -OutVariable 'Test')
## Tests whether they are equal, no return = success
If (-not $Test)
{
$True
}
Else
{
$False
}
}
PS C:\> $Obj1 = [PSCustomObject]#{
Property1 = 'Value1'
Property2 = 'Value2'
Property3 = 'Value3'
Property4 = 'Value4'
Property5 = 'Value5'
}
PS C:\> $Obj2 = [PSCustomObject]#{
Property1 = 'Value1'
Property2 = 'Value2'
Property3 = 'Value3'
Property4 = 'Value4'
Property5 = 'Value5'
}
PS C:\> Test-Objects $Obj1 $Obj2
True
PS C:\> $Obj2 | Add-Member -MemberType 'NoteProperty' -Name 'Prop6' -Value 'Value6'
PS C:\> Test-Objects $Obj1 $Obj2
False
If you'd like to test for equality for every object property, one at a time, in order to compare and contrast two objects and see which individual pieces are different, you can use the following function, adapted from this article on how to compare all properties of two objects in Windows PowerShell
Function Compare-ObjectProperties {
Param(
[PSObject]$leftObj,
[PSObject]$rightObj
)
$leftProps = $leftObj.PSObject.Properties.Name
$rightProps = $rightObj.PSObject.Properties.Name
$allProps = $leftProps + $rightProps | Sort | Select -Unique
$props = #()
foreach ($propName in $allProps) {
# test if has prop
$leftHasProp = $propName -in $leftProps
$rightHasProp = $propName -in $rightProps
# get value from object
$leftVal = $leftObj.$propName
$rightVal = $rightObj.$propName
# create custom output -
$prop = [pscustomobject] #{
Match = $(If ($propName -eq "SamAccountName" ) {"1st"} Else {
$(If ($leftHasProp -and !$rightHasProp ) {"Left"} Else {
$(If ($rightHasProp -and !$leftHasProp ) {"Right"} Else {
$(If ($leftVal -eq $rightVal ) {"Same"} Else {"Diff"})
})
})
})
PropName = $propName
LeftVal = $leftVal
RightVal = $rightVal
}
$props += $prop
}
# sort & format table widths
$props | Sort-Object Match, PropName | Format-Table -Property `
#{ Expression={$_.Match}; Label="Match"; Width=6},
#{ Expression={$_.PropName}; Label="Property Name"; Width=25},
#{ Expression={$_.LeftVal }; Label="Left Value"; Width=40},
#{ Expression={$_.RightVal}; Label="Right Value"; Width=40}
}
And then use like this:
$adUser1 = Get-ADUser 'Grace.Hopper' -Properties *
$adUser2 = Get-ADUser 'Katherine.Johnson' -Properties *
Compare-ObjectProperties $adUser1 $adUser2
Couple Interesting Notes:
How to Test if Element Has Property
How to Get Property Value by Name
How to Create a Custom PS Object
How to Create a Nested Conditional / Ternary Operator
How to Format Table with fixed Widths
Attempted to Colorize output with VT Escape Sequences or Write-PSObject, but couldn't get it to work with fixed column widths which took priority
Here's the function I used:
function Test-ObjectEquality {
param(
[Parameter(Mandatory = $true)]
$Object1,
[Parameter(Mandatory = $true)]
$Object2
)
return !(Compare-Object $Object1.PSObject.Properties $Object2.PSObject.Properties)
}
Examples:
PS C:\> $obj1 = [pscustomobject] #{ 'a' = '5'; 'b' = 7; };
PS C:\> $obj2 = [pscustomobject] #{ 'a' = '5'; 'b' = 7; };
PS C:\> Test-ObjectEquality $obj1 $obj2
True
PS C:\> $obj2 = [psobject] #{ 'a' = '5'; 'b' = 7; };
PS C:\> Test-ObjectEquality $obj1 $obj2
False
PS C:\> $obj2 = New-Object -TypeName PSObject -Property #{ 'a' = '5'; 'b' = 7; };
PS C:\> Test-ObjectEquality $obj1 $obj2
True
PS C:\> $obj2 = [pscustomobject] #{ 'c' = '6'; 'b' = 7; };
PS C:\> Test-ObjectEquality $obj1 $obj2
False
PS C:\> $obj2 = [pscustomobject] #{ 'a' = '5'; 'b' = 8; };
PS C:\> Test-ObjectEquality $obj1 $obj2
False
PS C:\> $obj2 = [pscustomobject] #{ 'a' = '5'; 'b' = 7; c = 8 };
PS C:\> Test-ObjectEquality $obj1 $obj2
False
PS C:\> $obj2 = [pscustomobject] #{ 'a' = '5'; 'b' = '7'; };
PS C:\> Test-ObjectEquality $obj1 $obj2
False
I certainly believe it's possible for this to miss things; however, if you look at what's in Properties you can see what's being compared for every property on an object:
PS C:\> $obj1.PSObject.Properties | Select-Object -First 1
MemberType : NoteProperty
IsSettable : True
IsGettable : True
Value : 5
TypeNameOfValue : System.String
Name : a
IsInstance : True
It's not often that I've cared about more than the MemberType, Name, TypeNameOfValue, or Value of an object's properties.
Also, note that if you really need to, you can compare .PSObject.Members instead of .PSObject.Properties. That will compare properties and methods, although you're only comparing the method calls and not the method definitions.
I wrote a function that checks for exact equality:
function Global:Test-IdenticalObjects
{
param(
[Parameter(Mandatory=$true)]$Object1,
[Parameter(Mandatory=$true)]$Object2,
$SecondRun=$false
)
if(-not ($Object1 -is [PsCustomObject] -and $Object2 -is [PsCustomObject))
{
Write-Error "Objects must be PsCustomObjects"
return
}
foreach($property1 in $Object1.PsObject.Properties)
{
$prop1_name = $property1.Name
$prop1_value = $Object1.$prop1_name
$found_property = $false
foreach($property2 in $Object2.PsObject.Properties)
{
$prop2_name = $property2.Name
$prop2_value = $Object2.$prop2_name
if($prop1_name -eq $prop2_name)
{
$found_property = $true
if($prop1_value -ne $prop2_value)
{
return $false
}
}
} # j loop
if(-not $found_property) { return $false }
} # i loop
if($SecondRun)
{
return $true
} else {
Test-IdenticalObjects -Object1 $Object2 -Object2 $Object1 -SecondRun $true
}
} # function

Select search return with powershell

I am building a multi-step local search engine with powershell that also allows you to email selected pieces of information.
I have got the search engine part down and the email part down, I just need to get the select part down.
So right now, you open the program and it prompts you to search for what you want. If I put in the query when, this is what is returned:
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 1/25/2017 3:29 PM 8357890 01 - Kiss Me When I'm Down.mp3
-a--- 1/24/2017 2:15 PM 7189290 09 - When You Love Someone.mp3
Now the goal is to select let's say 01 - Kiss Me When I'm Down.mp3, because I'm then going to put that into an $attachment variable, which will then send the song as an attachment. Is this achievable?
EDIT for clarity:
I've tried working with Select-Object to do this, but I can't get it to allow the user to select the song that they want. That is the goal of this, to allow the user to select the input they want.
This is an inelegant solution that adds an Index NoteProperty using the Add-Member cmdlet. As an example I used Get-ChildItem results:
$Items = Get-ChildItem C:\
$Index = 1
$Count = $Items.Count
foreach ($Item in $Items) {
$Item | Add-Member -MemberType NoteProperty -Name "Index" -Value $Index
$Index++
}
$Items | Select-Object Index, Attributes, LastWriteTime, Name | Out-Host
$Input = Read-Host "Select an item by index (1 to $Count)"
$Selected = $Items[$Input - 1]
Write-Host "You have selected $Selected"
I realise some good answers have been given, but the OP's post got me thinking about extracting the meta data for the MP3s...:
function getMP3Details() {
param ( [System.IO.FileInfo] $mp3file = $null )
[System.__ComObject] $Local:objShell = $null;
[System.__ComObject] $Local:objFolder = $null;
[System.__ComObject] $Local:objFile = $null;
[HashTable] $Local:objTags = #{ 0 = 'Name'; 1 = 'Size'; 13 = 'Artists'; 14 = 'Album'; 15 = 'Year'; 16 = 'Genre'; 20 = 'Authors'; 21 = 'Title'; 28 = 'Bit Rate'; }
[Int32] $Local:intTagIndex = 0;
[String] $Local:strTagName = '';
[String] $Local:strTagValue = '';
[PSCustomObject] $Local:objOutput = $null;
try {
if ( $mp3file -ne $null ) {
$objShell = New-Object -ComObject Shell.Application;
$objFolder = $objShell.NameSpace( $mp3file.DirectoryName );
$objFile = $objFolder.ParseName( $mp3file.Name );
$objOutput = New-Object -TypeName PSCustomObject;
foreach ( $intTagIndex in ($objTags.Keys | Sort-Object) ) {
$strTagName = $objTags[$intTagIndex];
$strTagValue = $objFolder.GetDetailsOf( $objFile, $intTagIndex );
Add-Member -InputObject $objOutput -MemberType NoteProperty -Name $strTagName -Value ( [String] ($strTagValue -replace '[^ -~]', '') );
} #foreach
Write-Output -InputObject $objOutput;
} #if
} #try
catch [System.Exception] {
# Do something.
'Error';
} #catch
return;
}
[String] $Local:strFolder = '<PATH TO ALBUM>';
[PSCustomObject[]] $Local:arrMP3Tracks = #();
[PSCustomObject] $Local:objSelectedTrack = $null;
try {
Get-ChildItem -LiteralPath $strFolder -File -Filter *.mp3 | Foreach-Object {
$arrMP3Tracks += getMP3Details -mp3file $_;
} #Foreach-Object
$objSelectedTrack = $arrMP3Tracks | Out-GridView -PassThru;
} #try
catch [System.Exception] {
# Do something.
'Error';
} #catch
exit 0;

Not able to publish custom cloudwatch metric

I am implementing a custom cloudwatch metric for a windows ec2 instance and here's my script for the same. This metric should report cloudwatch regarding the memory and pagefile details.
[CmdletBinding(DefaultParametersetName="credsfromfile", supportsshouldprocess = $true) ]
param(
[switch]$mem_util,
[switch]$mem_used ,
[switch]$mem_avail,
[switch]$page_used,
[switch]$page_avail,
[switch]$page_util,
[switch]$auto_scale,
[ValidateSet("bytes","kilobytes","megabytes","gigabytes" )]
[string]$memory_units = "none",
[switch]$from_scheduler,
[Parameter(Parametersetname ="credsinline",mandatory=$true)]
[string]$aws_access_id = "",
[Parameter(Parametersetname ="credsinline",mandatory=$true)]
[string]$aws_secret_key = "",
[Parameter(Parametersetname ="credsfromfile")]
[string]$aws_credential_file = [Environment]::GetEnvironmentVariable("AWS_CREDENTIAL_FILE"),
[string]$logfile = $null,
[Switch]$version
)
$ErrorActionPreference = 'Stop'
### Initliaze common variables ###
$accountinfo = New-Object psobject
$wc = New-Object Net.WebClient
$time = Get-Date
[string]$aaid =""
[string]$ask =""
$invoc = (Get-Variable myinvocation -Scope 0).value
$currdirectory = Split-Path $invoc.mycommand.path
$scriptname = $invoc.mycommand.Name
$ver = '1.0.0'
$client_name = 'CloudWatch-PutInstanceDataWindows'
$useragent = "$client_name/$ver"
### Logs all messages to file or prints to console based on from_scheduler setting. ###
function report_message ([string]$message)
{
if($from_scheduler)
{ if ($logfile.Length -eq 0 )
{
$logfile = $currdirectory +"\" +$scriptname.replace('.ps1','.log')
}
$message | Out-File -Append -FilePath $logfile
}
else
{
Write-Host $message
}
}
### Global trap for all exceptions for this script. All exceptions will exit the script.###
trap [Exception] {
report_message ($_.Exception.Message)
Exit
}
if ($version)
{
report_message "$scriptname version $ver"
exit
}
####Test and load AWS sdk
$ProgFilesLoc = (${env:ProgramFiles(x86)}, ${env:ProgramFiles} -ne $null)[0]
$SDKLoc = "$ProgFilesLoc\AWS SDK for .NET\bin\Net35"
if ((Test-Path -PathType Container -Path $SDKLoc) -eq $false) {
$SDKLoc = "C:\Windows\Assembly"
}
$SDKLibraryLocation = dir C:\Windows\Assembly -Recurse -Filter "AWSSDK.dll"
if ($SDKLibraryLocation -eq $null)
{
throw "Please Install .NET sdk for this script to work."
}
else
{
$SDKLibraryLocation = $SDKLibraryLocation.FullName
Add-Type -Path $SDKLibraryLocation
Write-Verbose "Assembly Loaded"
}
### Process parameterset for credentials and adds them to a powershell object ###
switch ($PSCmdlet.Parametersetname)
{
"credsinline" {
Write-Verbose "Using credentials passed as arguments"
if (!($aws_access_id.Length -eq 0 ))
{
$aaid = $aws_access_id
}
else
{
throw ("Value of AWS access key id is not specified.")
}
if (!($aws_secret_key.Length -eq 0 ))
{
$ask = $aws_secret_key
}
else
{
throw "Value of AWS secret key is not specified."
}
}
"credsfromfile"{
if ( Test-Path $aws_credential_file)
{
Write-Verbose "Using AWS credentials file $aws_credential_file"
Get-Content $aws_credential_file | ForEach-Object {
if($_ -match '.*=.*'){$text = $_.split("=");
switch ($text[0].trim())
{
"AWSAccessKeyId" {$aaid= $text[1].trim()}
"AWSSecretKey" { $ask = $text[1].trim()}
}}}
}
else {throw "Failed to open AWS credentials file $aws_credential_file"}
}
}
if (($aaid.length -eq 0) -or ($ask.length -eq 0))
{
throw "Provided incomplete AWS credential set"
}
else
{
Add-Member -membertype noteproperty -inputobject $accountinfo -name "AWSSecretKey" -value $ask
Add-Member -membertype noteproperty -inputobject $accountinfo -name "AWSAccessKeyId" -value $aaid
Remove-Variable ask; Remove-Variable aaid
}
### Check if atleast one metric is requested to report.###
if ( !$mem_avail -and !$mem_used -and !$mem_util -and !$page_avail -and !$page_used -and !$page_util)
{
throw "Please specify a metric to report exiting script"
}
### Avoid a storm of calls at the beginning of a minute.###
if ($from_scheduler)
{
$rand = new-object system.random
start-sleep -Seconds $rand.Next(20)
}
### Functions that interact with metadata to get data required for dimenstion calculation and endpoint for cloudwatch api. ###
function get-metadata {
$extendurl = $args
$baseurl = "http://169.254.169.254/latest/meta-data"
$fullurl = $baseurl + $extendurl
return ($wc.DownloadString($fullurl))
}
function get-region {
$az = get-metadata("/placement/availability-zone")
return ($az.Substring(0, ($az.Length -1)))
}
function get-endpoint {
$region = get-region
return "https://monitoring." + $region + ".amazonaws.com/"
}
### Function that creates metric data which will be added to metric list that will be finally pushed to cloudwatch. ###
function append_metric {
$metricdata = New-Object Amazon.Cloudwatch.Model.MetricDatum
$metricdata.metricname, $metricdata.Unit, $metricdata.value, $metricdata.Dimensions = $args
$metricdata.Timestamp = $time.ToUniversalTime()
return $metricdata
}
### Function that validates units passed. Default value of Megabytes is used###
function parse-units {
param ([string]$mem_units,
[long]$mem_unit_div)
$units = New-Object psobject
switch ($memory_units.ToLower())
{
"bytes" { $mem_units = "Bytes"; $mem_unit_div = 1}
"kilobytes" { $mem_units = "Kilobytes"; $mem_unit_div = 1kb}
"megabytes" { $mem_units = "Megabytes"; $mem_unit_div = 1mb}
"gigabytes" { $mem_units = "Gigabytes"; $mem_unit_div = 1gb}
default { $mem_units = "Megabytes"; $mem_unit_div = 1mb}
}
Add-Member -InputObject $units -Name "mem_units" -MemberType NoteProperty -Value $mem_units
Add-Member -InputObject $units -Name "mem_unit_div" -MemberType NoteProperty -Value $mem_unit_div
return $units
}
### Function that gets memory stats using WMI###
function get-memory {
begin {}
process {
$mem = New-Object psobject
$units = parse-units
[long]$mem_avail_wmi = (get-WmiObject Win32_OperatingSystem | select -expandproperty FreePhysicalMemory) * 1kb
[long]$total_phy_mem_wmi = get-WmiObject Win32_ComputerSystem | select -expandproperty TotalPhysicalMemory
[long]$mem_used_wmi = $total_phy_mem_wmi - $mem_avail_wmi
Add-Member -InputObject $mem -Name "mem_avail_wmi" -MemberType NoteProperty -Value $mem_avail_wmi
Add-Member -InputObject $mem -Name "total_phy_mem_wmi" -MemberType NoteProperty -Value $total_phy_mem_wmi
Add-Member -InputObject $mem -Name "mem_used_wmi" -MemberType NoteProperty -Value $mem_used_wmi
Add-Member -InputObject $mem -Name "mem_units" -MemberType NoteProperty -Value $units.mem_units
Add-Member -InputObject $mem -Name "mem_unit_div" -MemberType NoteProperty -Value $units.mem_unit_div
write $mem
}
end{}
}
### Function that writes metrics to be piped to next fucntion to push to cloudwatch.###
function create-metriclist {
param (
[parameter(Valuefrompipeline=$true)] $mem_info)
begin{
$group_name = ""
$instance_id = get-metadata("/instance-id")
$auto_scale_group = Get-ASAutoScalingGroup
foreach($as in $auto_scale_group)
{
$flag = 0
$group_name = $as.AutoScalingGroupName
foreach($ins in $as.Instances)
{
Write-Host $ins.InstanceId
Write-Host "`r`n"
if ($ins.InstanceId -eq $instance_id)
{
$flag = 1
break
}
}
if ($flag -eq 1)
{
break
}
Write-Host "---------------------------`r`n"
}
Write-Host $instance_id
Write-Host "`r`n"
Write-Host $group_name
Write-Host "`r`n"
$dimlist = New-Object Collections.Generic.List[Amazon.Cloudwatch.Model.Dimension]
$dims = New-Object Amazon.Cloudwatch.Model.Dimension
if ( $auto_scale)
{
$dims.Name = "AutoScalingGroupName"
$dims.value = $group_name
}
else
{
$dims.Name = "InstanceId"
$dims.value = $instance_id
}
$dimlist.Add($dims)
$pagefilessize = #{}
$pagefileusage = #{}
gwmi Win32_PageFileSetting | ForEach-Object{$pagefilessize[$_.name]=$_.MaximumSize *1mb}
gwmi Win32_PageFileUsage | ForEach-Object{$pagefileusage[$_.name]=$_.currentusage *1mb}
[string[]]$pagefiles = $pagefilessize.keys
}
process{
if ($mem_util)
{
$percent_mem_util= 0
if ( [long]$mem_info.total_phy_mem_wmi -gt 0 ) { $percent_mem_util = 100 * ([long]$mem_info.mem_used_wmi/[long]$mem_info.total_phy_mem_wmi)}
write (append_metric "MemoryUtilization" "Percent" ("{0:N2}" -f $percent_mem_util) $dimlist)
}
if ($mem_used)
{
write (append_metric "MemoryUsed" $mem_info.mem_units ("{0:N2}" -f ([long]($mem_info.mem_used_wmi/$mem_info.mem_unit_div))) $dimlist)
}
if ( $mem_avail)
{
write (append_metric "MemoryAvailable" $mem_info.mem_units ("{0:N2}" -f ([long]($mem_info.mem_avail_wmi/$mem_info.mem_unit_div))) $dimlist)
}
if ($page_avail)
{
for ($i=0; $i -le ($pagefiles.count - 1);$i++)
{
write (append_metric ("pagefileAvailable("+$pagefiles[$i]+")") $mem_info.mem_units ("{0:N2}" -f ((($pagefilessize[$pagefiles[$i]]- ($pagefileusage[$pagefiles[$i]]))/$mem_info.mem_unit_div))) $dimlist)
}
}
if ($page_used)
{
for ($i = 0; $i -le ($pagefiles.count -1); $i++)
{
write (append_metric ("pagefileUsed("+$pagefiles[$i]+")") $mem_info.mem_units ("{0:N2}" -f (($pagefileusage[$pagefiles[$i]])/$mem_info.mem_unit_div)) $dimlist)
}
}
if ($page_util)
{
for ($i=0; $i -le ($pagefiles.count -1);$i++)
{
if($pagefilessize[$pagefiles[$i]] -gt 0 )
{
write (append_metric ("pagefileUtilization("+$pagefiles[$i]+")") "Percent" ("{0:N2}" -f((($pagefileusage[$pagefiles[$i]])*100)/$pagefilessize[$pagefiles[$i]])) $dimlist)
}
}
}
}
end{}
}
### Uses AWS sdk to push metrics to cloudwathc. This finally prints a requestid.###
function put-instancemem {
param (
[parameter(Valuefrompipeline=$true)] $metlist)
begin{
$cwconfig = New-Object Amazon.CloudWatch.AmazonCloudWatchConfig
$cwconfig.serviceURL = get-endpoint
$cwconfig.UserAgent = $useragent
$monputrequest = new-object Amazon.Cloudwatch.Model.PutMetricDataRequest
$response = New-Object psobject
$metricdatalist = New-Object Collections.Generic.List[Amazon.Cloudwatch.Model.MetricDatum]
}
process{
if ($PSCmdlet.shouldprocess($metlist.metricname,"The metric data "+$metlist.value.tostring() +" "+ $metlist.unit.tostring()+" will be pushed to cloudwatch")){
$metricdatalist.add($metlist)
Write-Verbose ("Metricname= " +$metlist.metricname+" Metric Value= "+ $metlist.value.tostring()+" Metric Units= "+$metlist.unit.tostring())
}
}
end{
$monputrequest.namespace = "System/Windows"
if ($metricdatalist.count -gt 0 ) {
$cwclient = New-Object Amazon.Cloudwatch.AmazonCloudWatchClient($accountinfo.AWSAccessKeyId,$accountinfo.AWSSecretKey,$cwconfig)
$monputrequest.metricdata = $metricdatalist
$monresp = $cwclient.PutMetricData($monputrequest)
Add-Member -Name "RequestId" -MemberType NoteProperty -Value $monresp.ResponseMetadata.RequestId -InputObject $response
}
else {throw "No metric data to push to CloudWatch exiting script" }
Write-Verbose ("RequestID: " + $response.RequestId)
}
}
### Pipelined call of fucntions that pushs metrics to cloudwatch.
get-memory | create-metriclist | put-instancemem
When I execute this manually, I am getting an output as shown below:
'UserAgent' is a ReadOnly property.
But when I try get-memory | create-metriclist it is giving me the full details of memory and pagefile. Which means something is going wrong while trying to push to cloudwatch.
Can someone help me?

Is there a PowerShell equivalent tracert that works in version 2?

I'm using PSVersion 2.0 and I was wondering is there a equivalent to the traceroute for it?
I'm aware that on PowerShell v4 there is Test-NetConnection cmdlet to do tracert but v2?! It can be done like:
Test-NetConnection "IPaddress/HOSTaname" -TraceRoute
Thanks
As mentioned in the comment, you can make your own "poor-mans-PowerShell-tracert" by parsing the output from tracert.exe:
function Invoke-Tracert {
param([string]$RemoteHost)
tracert $RemoteHost |ForEach-Object{
if($_.Trim() -match "Tracing route to .*") {
Write-Host $_ -ForegroundColor Green
} elseif ($_.Trim() -match "^\d{1,2}\s+") {
$n,$a1,$a2,$a3,$target,$null = $_.Trim()-split"\s{2,}"
$Properties = #{
Hop = $n;
First = $a1;
Second = $a2;
Third = $a3;
Node = $target
}
New-Object psobject -Property $Properties
}
}
}
By default, powershell formats objects with 5 or more properties in a list, but you can get a tracert-like output with Format-Table:
Fixed a few bugs in " Mid-Waged-Mans-Tracert" Version, modularized it, and added some customization pieces. #MrPaulch had a great PoC.
function Invoke-Traceroute{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,Position=1)]
[string]$Destination,
[Parameter(Mandatory=$false)]
[int]$MaxTTL=16,
[Parameter(Mandatory=$false)]
[bool]$Fragmentation=$false,
[Parameter(Mandatory=$false)]
[bool]$VerboseOutput=$true,
[Parameter(Mandatory=$false)]
[int]$Timeout=5000
)
$ping = new-object System.Net.NetworkInformation.Ping
$success = [System.Net.NetworkInformation.IPStatus]::Success
$results = #()
if($VerboseOutput){Write-Host "Tracing to $Destination"}
for ($i=1; $i -le $MaxTTL; $i++) {
$popt = new-object System.Net.NetworkInformation.PingOptions($i, $Fragmentation)
$reply = $ping.Send($Destination, $Timeout, [System.Text.Encoding]::Default.GetBytes("MESSAGE"), $popt)
$addr = $reply.Address
try{$dns = [System.Net.Dns]::GetHostByAddress($addr)}
catch{$dns = "-"}
$name = $dns.HostName
$obj = New-Object -TypeName PSObject
$obj | Add-Member -MemberType NoteProperty -Name hop -Value $i
$obj | Add-Member -MemberType NoteProperty -Name address -Value $addr
$obj | Add-Member -MemberType NoteProperty -Name dns_name -Value $name
$obj | Add-Member -MemberType NoteProperty -Name latency -Value $reply.RoundTripTime
if($VerboseOutput){Write-Host "Hop: $i`t= $addr`t($name)"}
$results += $obj
if($reply.Status -eq $success){break}
}
Return $results
}
I must admit I wanted to see whether someone already did this.
You can use the .Net Framework to implement a not-so-poor-mans-traceroute as a Powershell Script
Here a primer, that works fast, but dangerous.
Also, no statistics.
#
# Mid-Waged-Mans-Tracert
#
$ping = new-object System.Net.NetworkInformation.Ping
$timeout = 5000
$maxttl = 64
$address = [string]$args
$message = [System.Text.Encoding]::Default.GetBytes("MESSAGE")
$dontfragment = false
$success = [System.Net.NetworkInformation.IPStatus]::Success
echo "Tracing $address"
for ($ttl=1;$i -le $maxttl; $ttl++) {
$popt = new-object System.Net.NetworkInformation.PingOptions($ttl, $dontfragment)
$reply = $ping.Send($address, $timeout, $message, $popt)
$addr = $reply.Address
$rtt = $reply.RoundtripTime
try {
$dns = [System.Net.Dns]::GetHostByAddress($addr)
} catch {
$dns = "-"
}
$name = $dns.HostName
echo "Hop: $ttl`t= $addr`t($name)"
if($reply.Status -eq $success) {break}
}
Edit:
Removed some of the danger by adding a catch statement.
The only danger that is still present is the fact that we only send a single request per hop, which could mean that we don't reach a hop due to a innocent package drop.
Resolving that issue remains a readers exercise.
Hint: (Think of loops within loops)
Bonus: We now attempt to get the dns entry of each hop!
With at least PS 5 you can
Test-Netconnection stackoverflow.com -TraceRoute