Select search return with powershell - 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;

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

Is it possible to autocomplete a Read-Host prompt with previous Get command output. Without saving an output file

I am writing a script to deploy VM hosts and I want to run a Get command to show them available options and then use their input with autocomplete from previous GET command. I do this because I want to avoid any typos that can be made during manual input.
I have tried using Select-string but I think it saves to .txt file and I don't want this to be saved in a txt file. I would rather have it saved in variable.
Get-VMHost | Select-Object -Property Name | Format-Table -Property Name
$VMHost = Read-Host -Prompt 'Please select the host for your VM
I expect the user to be able to autocomplete string with output from previously executed GET command. Please help if you can
Here is a New-ChoicePrompt function I use for similar purposes.
function New-ChoicePrompt { [cmdletBinding()]
param(
[parameter(mandatory=$true)]$Choices,
$Property,
$ReadProperty,
$ExprLabel,
[switch]$AllowManualInput,
[Scriptblock]$ReadPropertyExpr,
$ManualInputLabel = "Type my own"
)
if ( $choices[0] -isnot [string] -and !$property ) {"Please include New-ChoicePrompt -Property unless -Choices is an array of strings."; break}
if ( $choices[0] -is [string] -and ($property -or $ReadProperty) ) {"When New-ChoicePrompt -Choices is an array of strings, please omit -Property and -ReadProperty."; break}
#if ( $choices[0] -isnot [string] -and $allowManualInput ) {"When New-ChoicePrompt -Choices is a PSobject, please omit -AllowManualInput"; break}
$x = 0; $script:options = #()
$script:propty = $property
$script:choices = $choices
$manualInputLabel = "<" + $manualInputLabel + ">"
foreach ($item in $choices) { $value = $null
$x += 1
if ($property) { $value = $item | select -expand $property } `
else {$value = $item}
if ($readProperty) {
$readVal = $item | select -expand $readProperty
$row = new-object -type psObject -property #{Press = $x; 'to select' = $value; $readproperty = $readVal}
} ` #close if readProperty
elseif ($readPropertyExpr) `
{
$readVal = & $ReadPropertyExpr
$row = new-object -type psObject -property #{Press = $x; 'to select' = $value; $ExprLabel = $readVal}
}` #close if readPropertyExpr
else { $row = new-object -type psObject -property #{'to select' = $value; Press = $x} }
$script:options += $row
} #close foreach
if ($AllowManualInput) {
$row = new-object -type psObject -property #{'to select' = $manualInputLabel; Press = ($x + 1) }
$script:options += $row
} #close if allowManualInput
if ($ReadProperty) { $script:options | Select Press, "to select", $readproperty | ft -auto }
elseif ($ReadPropertyExpr) { $script:options | Select Press, "to select", $ExprLabel | ft -auto }
else { $script:options | Select Press, "to select" | ft -auto }
} #end function new-choicePrompt
Here is a usage example.
$vmhosts = Get-VMHost | sort Name
if ($vmhosts.count -gt 1) {
do {
new-choicePrompt -choices $vmhosts -property name
$in = read-host -prompt 'Please select a target host'
$range = $options | select -expand press
} #close do
until ($range -contains $in)
$selection = $options | where {$_.press -eq $in} | select -expand 'To select'
$choice = $choices | where {$_.#($propty) -eq $selection}
$vmHost = $choice
} else {$vmhost = $vmhosts} #close if multiple hosts
"Target host: " + $vmhost.name
If it was a function parameter, you could limit the values with [ValidateSet] like from here: https://www.mssqltips.com/sqlservertip/4205/powershell-parameters-part-ii--validateset-and-validatepattern/ It ends up supporting tab completion as well. If you set it to mandatory, it will prompt for it as well, if it's not given.
Function Pass-Set {
Param(
[ValidateSet("oro","plata")][string]$specificstring
)
Process
{
Write-Host "It must be one of two words; in this case $specificstring."
}
}
pass-set -specificstring oro
It must be one of two words; in this case oro.
pass-set -specificstring plata
It must be one of two words; in this case plata.
pass-set -specificstring plata2
Pass-Set : Cannot validate argument on parameter 'specificstring'. The argument "plata2" does not belong to the set "oro,plata" specified by the
ValidateSet attribute. Supply an argument that is in the set and then try the command again.
At line:1 char:26
+ pass-set -specificstring plata2
+ ~~~~~~
+ CategoryInfo : InvalidData: (:) [Pass-Set], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Pass-Set

PNP powershell SharePoint Online set modern page bannerimageurl

My goal is to use PNP commandlets to set the SharePoint online modern bannerimageurl property for a page.
First I get a list of the pages and their current title and bannerimageurl values
# Get alist of all pages and their banner URLs
$items = Get-PnPListItem -List "SitePages" -Fields ID,Title,BannerImageUrl
$items | %{new-object PSObject -Property #{Id=$_["ID"];Title=$_["Title"];BannerImageUrl=$_["BannerImageUrl"].Url}} | select ID,Title,BannerImageUrl
But even if I then run the following code to set the BannerImage of one page (say ID2)
Set-PnPListItem -List "SitePages" -Id 2 -Values #{"BannerImageUrl" = " https://contoso.sharepoint.com/sites/mycomsite3/bannerimages/bread-braid-tedster-sml.jpg";}
When I run the following again the item 2 shows up as having a changed BannerImageUrl
$items = Get-PnPListItem -List "SitePages" -Fields ID,Title,BannerImageUrl
$items | %{new-object PSObject -Property #{Id=$_["ID"];Title=$_["Title"];BannerImageUrl=$_["BannerImageUrl"].Url}} | select ID,Title,BannerImageUrl
BUT when I actually view the page in the browser that is item 2 there has been no change to the banner image ??
Please tell me what I'm doing wrong when I set the BannerImageUrl.
Your experience and knowledge greatly accepted.
I've written a PS Function for that exact same problem based on the JS Solution here, in case you still need it:
function UpdateBannerImage {
param(
[string]$listName,
[int]$itemId,
[string]$newBannerUrl
)
#get list item
$item = Get-PnPListItem -List $listName -Id $itemId -Fields LayoutWebpartsContent, BannerImageUrl
if($item["LayoutWebpartsContent"] -match 'data-sp-controldata="([^"]+)"'){
# get substring w/ regex
$temp = $item["LayoutWebpartsContent"] | Select-String -Pattern 'data-sp-controldata="([^"]+)"'
$content = $temp.Matches.Groups[1].Value
# replace [] bc sometimes it throws later
$content = $content.replace("[","[").replace("]","]")
# decode
$dec = [System.Web.HttpUtility]::HtmlDecode($content)
# from JSON
$jnContent = ConvertFrom-Json $dec
#set values
if (!$jnContent.serverProcessedContent) {
$jnContent.serverProcessedContent = {};
}
if (!$jnContent.serverProcessedContent.imageSources) {
$jnContent.serverProcessedContent.imageSources = New-Object PSObject;
$jnContent.serverProcessedContent.imageSources | add-member Noteproperty imageSource $newBannerUrl
}
if(!$jnContent.serverProcessedContent.imageSources.imageSource){
$jnContent.serverProcessedContent.imageSources | add-member Noteproperty imageSource $newBannerUrl
}
$jnContent.serverProcessedContent.imageSources.imageSource = $newBannerUrl
# need to always create new properties, otherwise nothing changes
$curTitle = "";
if($jnContent.properties){
$curTitle = $jnContent.properties.title;
}
$jnContent.properties = New-Object PSObject;
$jnContent.properties | add-member Noteproperty title $curTitle
$jnContent.properties | add-member Noteproperty imageSourceType 2
# to JSON
$newContent = $jnContent | ConvertTo-Json -Compress
$enc = [System.Web.HttpUtility]::HtmlEncode($newContent)
$enc = $enc.replace("{","{").replace(":",":").replace("}","}").replace("[","[").replace("]","]")
# replace full item property
$fullContent = $item["LayoutWebpartsContent"].replace("[","[").replace("]","]");
$fullContent = $fullContent -replace $content, $enc
$fullContent.replace("[","[").replace("]","]")
# set & update
$item["LayoutWebpartsContent"] = $fullContent
$item.Update()
# not really sure if needed, but also update bannerURL
Set-PnPListItem -List $listName -Id $itemId -Values #{"BannerImageUrl" = $newBannerUrl; }
}
}
new here sorry if i mess up the format, also uploaded for safety here :P

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

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).