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

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

Related

Powershell Calculated Property - Calling HashTable enumerator?

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

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;

How to search Windows Search Indexed in file

I have files indexed by the Windows Search service. I need function, which can find some string in text files. I have script in PowerShell, but it didn't work fine.
function search {
param($path, $word)
$c = $path + "\%"
$query = "SELECT
System.ItemName, System.ItemPathDisplay
FROM SystemIndex
WHERE System.ItemPathDisplay LIKE '$c' AND CONTAINS('$word')"
$ADOCommand = New-Object -ComObject ADODB.Command
$ADOConnection = New-Object -ComObject ADODB.Connection
$RecordSet = New-Object -ComObject ADODB.RecordSet
$ADOConnection.Open("Provider=Search.CollatorDSO;Extended Properties='Application=Windows';")
$RecordSet.Open($query, $ADOConnection)
try { $RecordSet.MoveFirst() }
catch [System.Exception] { "no records returned" }
while (-not($RecordSet.EOF)) {
if ($locatedFile) { Remove-Variable locatedFile }
$locatedFile = New-Object -TypeName PSObject
Add-Member -InputObject $locatedFile -MemberType NoteProperty -Name 'Name' -Value ($RecordSet.Fields.Item("System.ItemName")).Value
Add-Member -InputObject $locatedFile -MemberType NoteProperty -Name 'Path' -Value ($RecordSet.Fields.Item("System.ItemPathDisplay")).Value
$locatedFile
$RecordSet.MoveNext()
}
$RecordSet.Close()
$ADOConnection.Close()
$RecordSet = $null
$ADOConnection = $null
[gc]::Collect()
}
If $word = "Hello" it works fine for files where we have
*some text * Hello * some text*
in the file, but not when we have Hello without spaces like:
HelloWorld
We can't also search when $word is a phrase,for example "Hello World".
Anyone know how to fix it?
I believe the issue is with the CONTAINS in your query. You should add asterisk (*) wildcard character to the word that you search.
So instead of:
WHERE System.ItemPathDisplay LIKE '$c' AND CONTAINS('$word')
please try:
WHERE System.ItemPathDisplay LIKE '$c' AND CONTAINS('*$word*')

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?

Powershell Data.Table

Can anyone give some help with powershell tables?
The working part of the script
Function CheckWMI {
Param (
[Parameter(Mandatory=$True)]
$Computers
)
$CheckWMIResults = New-Object system.Data.DataTable
$Function = $CheckWMIResults.columns.add("ComputerName", [System.Type]::GetType("System.String") )
$Function = $CheckWMIResults.columns.add("Attempts", [System.Type]::GetType("System.Int32") )
$Function = $CheckWMIResults.columns.add("Result", [System.Type]::GetType("System.String") )
ForEach ($Computer in $Computers) {
$CheckWMIResults.Rows.Add($Computer,"0","Incomplete")
}
}
CheckWMI "192.168.1.8","192.168.1.7","192.168.1.6"
As you can see it takes each of the ip addresses and create a separate row for them.
Now how can I select one of those rows and update it, such as the count column of the second row?
There is no need to use a data structure so heavy as a DataTable for this. All you need is a simple collection like an array and the generic PSObject. The following rewrites your script above, then sets the Result of the first computer to Complete:
Function CheckWMI {
Param (
[Parameter(Mandatory=$True)]
[string[]]$Computers
)
$CheckWMIResults = #();
ForEach ($Computer in $Computers) {
$TempResults = New-Object PSObject;
$TempResults | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value $Computer;
$TempResults | Add-Member -MemberType NoteProperty -Name "Attempts" -Value 0;
$TempResults | Add-Member -MemberType NoteProperty -Name "Result" -Value "Incomplete";
$CheckWMIResults += $TempResults;
}
$CheckWMIResults;
}
$Results = CheckWMI -Computers "192.168.1.8","192.168.1.7","192.168.1.6"
$Results[0].Result = "Complete";
$Results;
If you do need type checking (which the DataTable gives you), define your own type.
add-type #"
public class WMIResults {
public string ComputerName;
public int Attempts;
public string Result;
}
"#
Function CheckWMI {
Param (
[Parameter(Mandatory=$True)]
[string[]]$Computers
)
$CheckWMIResults = #();
ForEach ($Computer in $Computers) {
$TempResults = New-Object WMIResults;
$TempResults.ComputerName = $Computer
$TempResults.Attempts = 0;
$TempResults.Result = "Incomplete";
$CheckWMIResults += $TempResults;
}
$CheckWMIResults;
}
$Results = CheckWMI -Computers "192.168.1.8","192.168.1.7","192.168.1.6"
$Results[0].Result = "Complete";
$Results;
See http://blogs.msdn.com/b/powershell/archive/2009/03/11/how-to-create-an-object-in-powershell.aspx and Get-Help Add-Type for more details on this second method ( you could use a struct instead of a class for trivial cases, but classes are generally a better idea).