Ping a /24 Subnet with Powershell

Ping a /24 Subnet with Powershell

A friend asked me how to ping a /24 subnet the other day. I thought it would be a good little blog post. This is a one-liner. Here we go.

1..254 | ForEach-Object {Test-Connection -ComputerName "10.10.1.$_" -Count 1 | Select-Object Source,Destination,Address,Status} | Format-Table -autosize

Let’s break this guy down. The 1..254 basically is every number between 1 and 254. We pipe that into a foreach-object loop. This will repeat whatever is inside the loop. In this case 254 times. Inside the loop, we use the Test-Connection command and set up the -ComputerName flag. Next, we set the subnet that we want to explore. Here it’s “10.10.1.$_” The $_ means the pipped object. The first run through the computer name will be “10.10.1.1” and so on. The next flag is the -count flag. This is how many times you want to try to connect to the device. I set it to 1 because the default is 3. Making it one is much faster and less confusing. Then I pipe that data into the select-object command. I select the source, destination, address, and status of the command. This gives me a clear and less confusing picture of everything. Then we close up the loop. Right here we can be finished. Sometimes though, Powershell’s shell is a pain the arse. The outlook would start looking like the this:

Lap-01582 10.10.1.97 TimedOut
Lap-01582 10.10.1.98 10.10.1.… Success
Lap-01582 10.10.1.99 10.10.1.… Success
Lap-01582 10.10.1.1… 10.10.1.… Success
Lap-01582 10.10.1.1… 10.10.1.… Success
Lap-01582 10.10.1.1… 10.10.1.… Success
Lap-01582 10.10.1.1… 10.10.1.… Success

To prevent this, we can use the Format-Table command with the -autosize flag. This will increase the size so it’s easier to read the information. The downside is it takes longer to get the feedback as you are piping all that information into a single command. Another thing you can do is export the information into a csv by using the export-csv command.

1..254 | ForEach-Object {Test-Connection -ComputerName "10.10.1.$_" -Count 1 | Select-Object Source,Destination,Address,Status} | export-csv -Path "$Env:USERPROFILE\Desktop\pingreport.csv" -NoClobber

This export command gives you the ability to place it wherever you want. I like it to go to either my desktop or my documents. So I use the $env:Userprofile variable. This Variable will give the shell’s current user’s profile and where it lives. Then you just tell it where to go after that. Notice I use the -NoClobber flag. This flag tells the export-csv to not add a line at the beginning of the csv stating it was generated by Powershell. Now I have a spreadsheet of this ping report.

I hope this helps, as always, if you need help, feel free to reach out.

Mitel Director NCC logs

Mitel Director NCC logs

One of the things that have helped me throughout my struggles with Mitel/Shoretel is the NCC logs inside the shoreline data folder. These logs can be hard to read, and very aggravating. Let’s look at a log example.

Below is a table that explains each of the call letters at the beginning of the log.

Call LettersEvent NameWhat it does
C-CECall Creation EventCall Initiation
L-CELeg Create EventFollows a C-CE for every call setup. internal transfers (Blind Transfers)
L-IELeg Info EventFollows a C-CE, L-CE. Leg info provides information on other parties in the call.
C-SECall State EventState of call in progress, all parties. This includes RingBack, Offering, established, etc
LSELeg State EventFollows a C-SE to inform the leg state changes.
L-DELeg Destroy EventCall Teardown. The Leg is destroyed.
C-DECall Destroy EventThe call is destroyed by the user or system hung up.
G-MSTMedia State EventMedia stats for the terminated call leg. Every RTP stream also has a media state event.

Here are examples of what each part will look like.

Trunk call Leg to PSTN40000001
Internal call Leg200001B3
Call GUID00020000-1aae-4f05-9cce-0010491e1b95
PBX Responding to Partyncc_media_ctl
Sip Info“sip:3067@10.10.60.2:5441”
Some Info(s:8757, r:8763, l:0)
Call Quality Info(j:0,u:0,o:1)
Audio Type2(ULaw)

Now let’s look at this example below and take it apart from a little. I’m no expert but I have gone through a few hundred of these logs.

  • 00:21:06.511 ( 5268: 7496) C-CE: 200001B3 ” guid=000a0000-48d7-5dea-a92f-005056a6c1e2” (“4003″,”Raleigh Distribution 3″,0xC) 00000000,SDP:N,ipCDS:0x0000000A, flgs:0x00000000, cd:0x00000000,”” “sip:4003@10.10.29.162:5441
  • 00:21:06.511 ( 5268: 7496) L-CE: 200001B3 ” guid=000a0000-48d7-5dea-a92f-005056a6c1e2” 010710EE(00000000) ,Req:00000000, 1, Flgs:00000000(Null) “sip:4003@10.10.29.162:5441
  • 00:21:09.421 ( 5268: 7496) L-IE: 200001B3 ” guid=000a0000-48d7-5dea-a92f-005056a6c1e2” 010710EE,rsn:2(Originate),1, (“9+19105772816″,””,0x8(Number)), C(“4003″,”Raleigh Distribution 3”, 0xC(NameNumber)) contact=sip:TGrp_5,p77@10.10.35.10:5441, “sip:4003@10.10.29.162:5441
  • 00:21:09.421 ( 5268: 7496) C-SE: 200001B3 ” guid=000a0000-48d7-5dea-a92f-005056a6c1e2” 5(Established), sd:0,04:21:09.435 (UTC) “sip:4003@10.10.29.162:5441” “”
  • 00:21:09.421 ( 5268: 7496) L-SE: 200001B3 ” guid=000a0000-48d7-5dea-a92f-005056a6c1e2” 010710EE, 5(Established), 536870912, 04:21:09.435 (UTC) “sip:4003@10.10.29.162:5441
  • 00:21:29.294 ( 5268: 7496) C-SE: 200001B3 ” guid=000a0000-48d7-5dea-a92f-005056a6c1e2” 17(FarEndAnswered), sd:0,04:21:09.435 (UTC) “sip:4003@10.10.29.162:5441” “”
  • 00:22:06.746 ( 5268: 7496) L-DE: 200001B3 ” guid=000a0000-48d7-5dea-a92f-005056a6c1e2” 010710EE, rsn:1(Normal), C(“4003″,”Raleigh Distribution 3”,0xC) TrGp=5 “sip:4003@10.10.29.162:5441
  • 00:22:06.746 ( 5268: 7496) C-DE: 200001B3 ” guid=000a0000-48d7-5dea-a92f-005056a6c1e2” 1, (Normal) “sip:4003@10.10.29.162:5441
  • 00:22:06.975 ( 5268: 7496) G-MST: 200001B3 ” guid=000a0000-48d7-5dea-a92f-005056a6c1e2” (“10.10.41.93″,”10.10.35.10”), (0, 0), 2(ULaw), rsn:1,04:21:11.710 (UTC), pl:20,(s:2761, r:2764, l:0), (j:0,u:0,o:0) flgs:0x00000000 “sip:4003@10.10.29.162:5441“,vpn:0

I hope this is helpful. I’m no expert, but I do follow the logs to see where they lead me.

Local Site Found by a Universal device

Local Site Found by a Universal device

We have multiple locations with backup hosted sites. These sites are designed to allow the end-user to access the information they need when the network is down. They are set up with a local database and some basic asp.net coding. However, the devices that communicate with the site must be able to go between multiple locations. This means no hard-coded links as the hosting device has a different name at each location. DNS might not be available as well. The chaos this brings! There is a solution though. Here are the requirements for the solution we created.

  1. The hosting server can be a different name, but the IP address must end in the same number. In our example, it is .5
  2. The IP address must be a /24 subnet.
  3. The /site.html must be the same across the board.
    1. For example, the site can be http://10.10.1.5/thissite.html at one location and be http://10.10.13.5/thissite.html at another.

lets look at the powershell script really quickly and break it down.

The Script

$IPaddress = (Get-NetIPConfiguration -Detailed | Where-Object {$null -ne $_.IPv4DefaultGateway})[0].ipv4address.ipaddress
Start-Process -FilePath 'C:\Program Files\Internet Explorer\iexplore.exe' -ArgumentList "https://$($IPaddress.split('.')[0]).$($IPaddress.split('.')[1]).$($IPaddress.split('.')[2]).5/login.aspx"

What we are doing with this script is grabbing a detailed report on all the network IP configurations on the local computer. By default, an active network will have a default gateway assigned to it. This can be statically assigned or it can be automatically assigned. Either way, it goes, it’s assigned.

To narrow down the results we use a where-object command to compare null to the IPv4DefaultGateway. If it is null, we don’t want it. If it isn’t, then we want it. After that, we ask for the first result using the [0]. Most companies use the first network as the main network and the second network as a backup. That’s what we do at our company. Also most of the time the second network is a wifi network.

Next, we Ask for the IPv4Address and ask for just the IP address with .IPv4Address.IpAddress. Once we have all that information, we place it into a variable.

Now it’s time to start IE. You can set the browser to whatever program you like, but for now, we are still using IE for grandpa’s coded sites. We do this using the Start-Process command. We tell start-process the file path. This is where you can set google chrome, a 64 bit, edge, etc… It doesn’t matter what the browser is, as long as you point to one.

The Argument List is an important factor here. We give the site information here. We start off with our “https:// Then we do magic. We $($IP.address.split(‘.’)[0]. The act of using two $ is very important inside a double-quoted string. The reason why is we are stating everything inside $() is it’s own thing. We can run different commands inside $() and the output will output at that point in the string. Thus we are breaking the IP address string down by the . and finding each array point. Then after we create that point, we finish it off with a . and start our next $(). We do this until we reach our last octet. Which will be whatever the default IP address is. Finally finishing off with the site name. In this example, login.aspx. Below is the start process part of the command. Take a close look at the argument list. Assume the IP address is 10.10.1.15. This would start a site looking at “https://10.10.1.5/login.aspx”.

Start-Process -FilePath 'C:\Program Files\Internet Explorer\iexplore.exe' -ArgumentList "https://$($IPaddress.split('.')[0]).$($IPaddress.split('.')[1]).$($IPaddress.split('.')[2]).5/login.aspx"

The final step is to set your lnk to open the PowerShell script with a -bypass flag. This way it will execute. Some companies do not allow PowerShell to execute. In that case, this script does not help. You can however use the match version of this script found below.

@echo off
for /f "usebackq tokens=2 delims=:" %%f in (`ipconfig ^| findstr /c:"IPv4 Address"`) do set "ip=%%f"

for /f "tokens=1-4 delims=. " %%a in ("%ip%") do (
set octetA=%%a
set octetB=%%b
set octetC=%%c
set octetD=%%d
)

start "CMD" /D "C:\Windows\System32\" /max "C:\Program Files\Internet Explorer\iexplore.exe" "https://%octetA%.%octetB%.%octetC%.5/login.aspx"
exit

Conclusion

The final idea is to have the link to point to an organic system that can read the current IP structure and point the link to the correct page. This will allow that single roaming device to find those local sites quickly assuming you have a pre-planned infrastructure in place.

Password Notifications

Password Notifications

I wanted to share a little script that has changed the world at my current company. It’s a simple password email notification script. What it does is grabs the users’ whos passwords about to expire. Then it emails that user with instructions on how to reset their password. Then it sends an email to the user’s manager with a list of when the user’s password will expire. Then finally it sends an email to another person with a complete list of all expiring users and when they will expire. After the user hits zero, the script no longer cares about that person. This is to prevent lots of headaches in the long run with on leave and whatnots. The best thing to do is to set up a scheduled task on a server with rsats. The computer will also need a relay setup on the exchange server.

The Script

param (
    [parameter(Helpmessage = "Return Email", Mandatory = $true)][string]$ReturnEmail,
    [parameter(Helpmessage = "Email Sent to IT", Mandatory = $true)][string]$ITEmail,
    [parameter(Helpmessage = "Mail Server", Mandatory = $true)][string]$MailServer,
    [parameter(Helpmessage = "Employee Organizational Unit", Mandatory = $true)][string]$EmployeeOU,
    [parameter(Helpmessage = "Training Link", Mandatory = $true)][string]$TrainingLink,
    [parameter(Helpmessage = "Training Link", Mandatory = $true)][string]$DaysWithinExpiration
)
$MaxPwdAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.Days
$expiredDate = (Get-Date).addDays(-$MaxPwdAge)
$emailDate = (Get-Date).addDays( - ($MaxPwdAge - $DaysWithinExpiration))
$ExpiredUsers = Get-ADUser -Filter { (PasswordLastSet -lt $emailDate) -and (PasswordLastSet -gt $expiredDate) -and (PasswordNeverExpires -eq $false) -and (Enabled -eq $true) } -Properties DisplayName, PasswordNeverExpires, Manager, PasswordLastSet, Mail, "msDS-UserPasswordExpiryTimeComputed" -SearchBase "$EmployeeOU" | Select-Object DisplayName, samaccountname, manager, PasswordLastSet, @{name = "DaysUntilExpired"; Expression = { $_.PasswordLastSet - $ExpiredDate | Select-Object -ExpandProperty Days } }, @{name = "EmailAddress"; Expression = { $_.mail } }, @{Name = "ExpiryDate"; Expression = { [datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed") } } | Sort-Object PasswordLastSet
$ExpiredUsers
foreach ($ExpiredUser in $ExpiredUsers) {
    $ReturnHTML = @" 
<html> 
<head>
<style>
body {
    Color: #252525;
    font-family: Verdana,Arial;
    font-size:11pt;
}
h1 {
    text-align: center;
    color:#C8102E;
    Font-size: 34pt;
    font-family: Verdana, Arial;
}
h2 {
    text-align: center;
    color:#9EA2A2;
    Font-size: 20pt;
}
h3 {
    text-align: center;
    color:#211b1c;
    Font-size: 15pt;
}
h4 {
    text-align: center;
    color:#242526;
    Font-size: 15pt;
}
a:link {
    color:#C8102E;
    text-decoration: underline;
    cursor: auto;
    font-weight: 700;
}
a:visited {
    color:#C8102E;
    text-decoration: underline;
    cursor: auto;
    font-weight: 700;
}
</style>
</head>
<body>
Dear $($ExpiredUser.DisplayName),<br><br>
Your password was last set on <i>$($ExpiredUser.PasswordLastSet)</i>. This means your password will expire in <b>$($ExpiredUser.DaysUntilExpired) Days</b>. Please reset your password before <b>$($ExpiredUser.ExpiryDate)</b><br>
<ol>
<li>Find a computer connected to the network</li>
<li>if the computer is logged in, on your keyboard press, <b>ctrl+alt+del</b> at the same time. </li>
<li>Select <b>Change Password</b></li>
<li>replace the username section with <b>$($ExpiredUser.samaccountname)</b>.</li>
<li>Enter your old password in the old password location.</li>
<li>Enter a new password in the new password location.</li>
<li>Confirm your new password in the confirm password location</li>
<li>Click Enter</li>
</ol>
<br>
<hr>
<br>
For more Information on how to change your password Please watch these videos.<br>
<a href='$TrainingLink'>How to change your Password.</a><br><br>

Thank you<br>
IT Department
</body> 
</html> 
"@  
    Send-MailMessage -To $($ExpiredUser.EmailAddress) -From "$ReturnEmail" -Subject "$($ExpiredUser.DisplayName) Password Notification" -BodyAsHtml $ReturnHTML -SmtpServer $MailServer
}
$Managers = $ExpiredUsers | Select-Object -ExpandProperty manager -Unique
foreach ($Manager in $Managers) {
    $ReportingtoHTML = $ExpiredUsers | Where-Object { $_.manager -eq $Manager } | Select-Object -Property DisplayName, @{name = "EmailAddress"; Expression = { $_.mail } }, @{label = "DaysUntilExpired"; expression = { "$($_.DaysUntilExpired) Days" } }, ExpiryDate | ConvertTo-Html -Fragment -As Table
    $Manager = Get-ADUser $Manager -Properties DisplayName, PasswordNeverExpires, Manager, PasswordLastSet, "msDS-UserPasswordExpiryTimeComputed" | Select-Object DisplayName, samaccountname, mail, manager, PasswordLastSet, @{name = "DaysUntilExpired"; Expression = { $_.PasswordLastSet - $ExpiredDate | Select-Object -ExpandProperty Days } }, @{name = "EmailAddress"; Expression = { $_.mail } }, @{Name = "ExpiryDate"; Expression = { [datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed") } } | Sort-Object PasswordLastSet
    $ManagerReturnHTML = @" 
<html> 
<head>
<style>
h1 {
    text-align: center;
    color:#C8102E;
    Font-size: 34pt;
    font-family: Verdana, Arial;
}
h2 {
    text-align: center;
    color:#9EA2A2;
    Font-size: 20pt;
}
h3 {
    text-align: center;
    color:#211b1c;
    Font-size: 15pt;
}
h4 {
    text-align: center;
    color:#242526;
    Font-size: 15pt;
}
a:link {
    color:#C8102E;
    text-decoration: underline;
    cursor: auto;
    font-weight: 700;
}
a:visited {
    color:#C8102E;
    text-decoration: underline;
    cursor: auto;
    font-weight: 700;
}
body {
width:75%;
background-color:#ffffff;}
table {border-collapse: collapse; border: 1px solid rgb(45,41,38); text-align: Center;}
th {background-color: #f7a1af; text-align: Center;}
tr:nth-child(even) {background-color: #f2f2f2;}
tr {text-align: Center;}
td {border:1px solid rgb(45,41,38);text-align: Center;}
</style>
</head>
<body style="font-family:verdana;font-size:13"> 
Dear $($Manager.DisplayName),<br><br>
Here is a list of all employees that are reporting to you whose passwords are to expire within the next 14 days. Please have these employees reset their passwords <b>before</b> their expirydate. Please note that we have also sent an email to the employee with the instructions on how to reset their password.<br>
<br>
$ReportingtoHTML
<br>
The below videos have been sent to these employees. If the employee needs help, refer them to these videos or instruct them how to reset their passwords.<br>
<a href='$TrainingLink'>How to change your Password.</a><br><br>
<br>

Thank you<br>
IT Department
</body> 
</html> 
"@  
    Send-MailMessage -To $($Manager.EmailAddress) -From "$ReturnEmail" -Subject "User Password Notification" -BodyAsHtml $ManagerReturnHTML -SmtpServer $MailServer
}
$ITReturnHTML = $ExpiredUsers | Select-Object @{label = "Name"; expression = { $_.Displayname } }, @{Label = "UserName"; expression = { $_.samaccountname } }, EmailAddress, @{label = "Manager"; expression = { $(($_.Manager.Split(',')).split('=')[1]) } }, ExpiryDate, @{Label = "Days"; expression = { $_.DaysUntilExpired } } | ConvertTo-Html -Fragment -As Table
$ItReportReturnHTML = @" 
<html> 
<head>
<style>
h1 {
    text-align: center;
    color:#C8102E;
    Font-size: 34pt;
    font-family: Verdana, Arial;
}
h2 {
    text-align: center;
    color:#9EA2A2;
    Font-size: 20pt;
}
h3 {
    text-align: center;
    color:#211b1c;
    Font-size: 15pt;
}
h4 {
    text-align: center;
    color:#242526;
    Font-size: 15pt;
}
a:link {
    color:#C8102E;
    text-decoration: underline;
    cursor: auto;
    font-weight: 700;
}
a:visited {
    color:#C8102E;
    text-decoration: underline;
    cursor: auto;
    font-weight: 700;
}
body {
background-color:#ffffff;}
table {border-collapse: collapse; border: 1px solid rgb(45,41,38); text-align: Center;}
th {background-color: #54c6ff; text-align: Center;}
tr:nth-child(even) {background-color: #f2f2f2;}
tr {text-align: Center;width:20%;}
td {border:1px solid rgb(45,41,38);text-align: Center;}
</style>
</head>
<body style="font-family:verdana;font-size:13"> 
Dear IT,<br><br>
Please see the expiring/expired password list below.
<br><br>
$ITReturnHTML
<br>
The below videos have been sent to these employees. If the employee needs help, refer them to these videos or instruct them how to reset their passwords.<br>
<a href='$TrainingLink'>How to change your Password.</a><br><br>
<br>

Thank you<br>
IT Department
</body> 
</html> 
"@  
Send-MailMessage -To $ITEmail -From "$ReturnEmail" -Subject "Password Expiry Notification" -BodyAsHtml $ItReportReturnHTML -SmtpServer $MailServer
$Admins = Get-ADUser -Filter { (samaccountname -like "*_adm") -and (PasswordLastSet -lt $emailDate) -and (PasswordLastSet -gt $expiredDate) -and (PasswordNeverExpires -eq $false) -and (enabled -eq $true) } -Properties *
Foreach ($Admin in $Admins) {
    $Username = $Admin.samaccountname -replace "(_.*)", ""
    $User = Get-ADUser -Identity $Username -Properties *
    $ReturnHTML = @" 
<html> 
<head>
<style>
body {
    Color: #252525;
    font-family: Verdana,Arial;
    font-size:11pt;
}
h1 {
    text-align: center;
    color:#C8102E;
    Font-size: 34pt;
    font-family: Verdana, Arial;
}
h2 {
    text-align: center;
    color:#9EA2A2;
    Font-size: 20pt;
}
h3 {
    text-align: center;
    color:#211b1c;
    Font-size: 15pt;
}
h4 {
    text-align: center;
    color:#242526;
    Font-size: 15pt;
}
a:link {
    color:#C8102E;
    text-decoration: underline;
    cursor: auto;
    font-weight: 700;
}
a:visited {
    color:#C8102E;
    text-decoration: underline;
    cursor: auto;
    font-weight: 700;
}
</style>
</head>
<body>
Dear $($User.DisplayName),<br><br>
Your Admin password was last set on <i>$($Admin.PasswordLastSet)</i>. This means your password will expire in <b>$($admin.DaysUntilExpired) Days</b>. Please reset your password before <b>$($Admin.ExpiryDate)</b><br>
<ol>
<li>Find a computer connected to the network</li>
<li>if the computer is logged in, on your keyboard press, <b>ctrl+alt+del</b> at the same time. </li>
<li>Select <b>Change Password</b></li>
<li>replace the username section with <b>$($Admin.samaccountname)</b>.</li>
<li>Enter your old password in the old password location.</li>
<li>Enter a new password in the new password location.</li>
<li>Confirm your new password in the confirm password location</li>
<li>Click Enter</li>
</ol>
<br>
<hr>
<br>
For more Information on how to change your password Please watch these videos.<br>
<a href='$TrainingLink'>How to change your Password.</a><br><br>

Thank you<br>
IT Department
</body> 
</html> 
"@  
    Send-MailMessage -To $($User.EmailAddress) -From "$ReturnEmail" -Subject "$($User.DisplayName) Password Notification" -BodyAsHtml $ReturnHTML -SmtpServer $MailServer
}
PSBoundParameters – Credentials

PSBoundParameters – Credentials

The PSBoundParameter is an amazing tool that will clear your mind of worries about non-mandatory parameter sets. In my Super Help Desk module I use the credential flag. Inside this flag, I tell the system to use the credentials provided or the currently running credentials.

I do this by testing the $PSBoundParameter.ContainsKey(‘Credential’). What this basically does is test the bounded parameters coming in to see if it contains a keyword. In this case I’m looking for Credential.

if ($PSBoundParameters.ContainsKey('Credential')) {
    Do-Something -Credential $Credential
} else {
    Do-Something
}

By having the flag set inside the parameter area not set to mandatory, you can test to see if they want to use a special set of credentials or not.

[Parameter(HelpMessage = "Allows for custom Credential.")][System.Management.Automation.PSCredential]$Credential

You can use other parameters as well but the credentials are one of the best usages for $PSboundparameter.containskey(). I do hope you enjoy using this little trick in your future scripts! Let me know what you create.