This past week I have had multiple terminals up with different clients on different terminals connected to different Microsoft services. I quickly realized that I needed to know if I was already connected or not to each Microsoft service. I knew that Get-PPSession was a key to this, but what surprised me was azure and msols didn't have a PPSession to review. I use 4 different connections on a daily basis. The Msol, Exchange Online, Compliance, and Azure AD. Here is how you can quickly test each service to see if it's connected.
Msol Service
With Msol we want to test a command to see if it works. We output that command to null. The command I found to be the quickest for this is Get-MsolDomain. Once we run the command we check to see how successful that command was with $?. I like to put things into arrays for future use. So, I dropped it into a $Results and then present those display those results.
With Exchange online, we can use the Get-PSSession. We search the connection uri for outlook.office365. We also look at the state to be opened. We then ask if how many sessions there are. if it's greater than 0, then we are good to go. Believe it or not, this is a one-liner.
With Compliance, it's similar to the exchange online. We are using the Get-PSSession again. This time we are looking for the word compliance and the state open as well. Once again, this can be a one-liner.
Finally, we come to the Azure AD, Just like the Msol, we have to check it using a command. I have seen people use the Get-AzureADTenantDetail commandlet. So we will use that commandlet and pipe it into out-null. Then we confirm if it worked with the $? command.
With everything that happened with Facebook yesterday, I began to wonder where does my query goes when I type in facebook.com. So, I did a few things and found out. The first thing I did was resolve the name facebook.com to an IP address, or group of IP addresses in this case with the command resolve-dnsname.
Resolve-DnsName -Name facebook.com
Then from there, I used the site, ip-api.com to pull the location information of the IP address. This awesome little site gives you city, state, country, zip codes, and even the ISP information of an IP address.
$Info = Invoke-RestMethod -Method Get -URI "http://ip-api.com/json/$IP"
That's the base of the code that we will explore. It's very straightforward, but I want to clean it up some. I want to make a Get GEO IP information and a Resolve DNSname to Geo IP. I want it to all work together even if there is multiple IP addresses and hosts names. So, lets start off with the scripts and break them down. This will contain two functions for what we are wanting.
Get-SHDGeoIP
function Get-SHDGeoIP {
[cmdletbinding()]
param (
[parameter(Mandatory = $true)][ipaddress[]]$IPAddress,
[switch]$Complete
)
foreach ($IP in $IPAddress) {
$Info = Invoke-RestMethod -Method Get -URI "http://ip-api.com/json/$IP"
if ($Complete) {
$Info
}
else {
[pscustomobject]@{
IPAddress = $info.Query
City = $Info.city
State = $Info.regionName
Country = $Info.country
ISP = $Info.isp
}
}
}
}
This script is going to pull the geo information for us. We start off with the parameters. We are testing the parameters to see if the IP address is an valid IP address. We do that with [ipaddress]. This tests for both IPv4 and IPv6. We tell it to be a array of IPaddresses with the [] inside of it. [ipaddress[]]. Just for cleaner fun, I have a switch for a complete information dump. This way
Since this is an array of IP addresses, we will start a foreach loop for each IP address in the array. We start the foreach loop by grabbing the IP information. If the user selected complete, we just dump the information we gathered to the user. if they didn't select complete, we create a custom object with the IP address, city, state, country and ISP information.
The next function uses the previous function and combines it with Resolve-DnsName. We start off with a list of strings for our hostname parameter and our complete parameter. We start our loop like before of the host names. Then we use the Get-SHDGeoIP -IPAddress command with the Resolve-DnsName -Name and the link name. We then select the IP addresses which is an array. We place that array inside the Get-SHDGeoIP and bam, we have our information. Converting a hostname like Facebook.com to IP information.
With these two little scripts, you will be able to find quick information about a website and where it is being hosted. For example, this site is hosted in new jersey. I personally didn't know that.
This little script logs public IP address changes. The original design was to email out. I'll make sure to point out when and where to put that code. So the idea is simple. It checks to see if it's public IP address has changed using an invoke-webrequest. If it has changed, it checks to see if a log has been made. If it has been made, it updates the log, if it hasn't been made, it creates the log. Then when the IP address changes back, it backs up the logs and stops. Lets take a look at the script.
The Script
Param (
[string]$IPAddress = "Your IP Address"
)
$IP = (invoke-webrequest -Uri "http://ifconfig.me/ip" -UseBasicParsing).content
If (!(Test-Path C:\temp)) { New-Item -Path "c:\temp" -ItemType Directory }
if (!($IP -like "$IPAddress")) {
if (!(Test-Path c:\temp\IPTrigger.log )) {
#Email Code Goes Here
$Datetime = (Get-Date).ToString("yyyy-MM-dd_HH-mm")
"Datetime,IP" > c:\temp\IPTrigger.log
"$datetime,$IP" >> c:\temp\IPTrigger.log
}
else {
$Datetime = (Get-Date).ToString("yyyy-MM-dd_HH-mm")
"$datetime,$IP" >> c:\temp\IPTrigger.log
}
}
else {
if (Test-Path c:\temp\IPTrigger.log) {
$Datetime = (Get-Date).ToString("yyyy-MM-dd_HH-mm")
Copy-Item -Path c:\temp\IPTrigger.log -Destination "c:\temp\IPTrigger_$($datetime).log.bak"
Remove-Item -Path c:\temp\IPTrigger.log
#Email Code goes here with attachment of the log bak.
}
}
The Breakdown
We grab the IP address from the Paramters. This way you can have the script called Public-IPChecker -IPAddress Something from your task scheduler. After we get what the IP Address should be we get what It is with the Invoke-webrequest command.
There are a hand full of sites that grabs the IP address for you. I personally like Ifconfig.me because it places the IP address as the content. Thus no sorting through the HTML to get the IP address.
Next we ask if it's the IP address. Then we ask if the log has been made.
if (!($IP -like "$IPAddress")) {
if (!(Test-Path c:\temp\IPTrigger.log )) {}
}
If it doesn't exist, we create it by getting the DateTime into a file friendly format. and Piping that information into the log file along with the current IP address.
If the IP addresses do match then we test to see if the log file is there. If it is there we create a bak of the log file and remove the old one. This is when you can send an email saying it's back up. If it doesn't exist, we do nothing.
I have been using Quser for years and I was wondering how to parse the data out to Powershell. The best way to do this is to convert the output into a csv and then convert it from the csv to a psobject. Let us get started. First we output the quser to a variable.
In this example, we are going to grab the local computer's logged-in information. Here is what the output looks like:
PS C:\Users\david> Quser /server:"$($env:COMPUTERNAME)" 2> $null
USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
david console 11 Active 1:05 9/5/2021 9:29 PM
susan 12 Disc 1:05 9/6/2021 11:14 AM
Next, we replace the first (^) > with nothing. It doesn't look like much but it adds the spacing needed.
$Users = $Users -Replace '^>',''
Here is the output.
USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
david console 11 Active 1:08 9/5/2021 9:29 PM
susan 12 Disc 1:08 9/6/2021 11:14 AM
Now we will replace the tabs with commas.
$Users = $Users -Replace '\s{2,}',','
USERNAME,SESSIONNAME,ID,STATE,IDLE TIME,LOGON TIME
david,console,11,Active,1:08,9/5/2021 9:29 PM
susan,12,Disc,1:08,9/6/2021 11:14 AM
From here we want to go through each line and remove the last item if it has too many spaces. This happens when you run this command on a server. You will need to go through each line by line with a foreach loop.
Splitting the user by the comma, we need only 4 not 5. So we remove the first of the last object. If it is 4, we do nothing special and just reintroduce the original $user.
USERNAME,SESSIONNAME,ID,STATE,IDLE TIME,LOGON TIME
david,console,11,Active,1:08,9/5/2021 9:29 PM
susan,,12,Disc,1:08,9/6/2021 11:14 AM
Now we take that $user csv and convert it to a psobject using convertfrom-csv.
$users = $users | ConvertFrom-Csv
USERNAME : david
SESSIONNAME : console
ID : 11
STATE : Active
IDLE TIME : 1:08
LOGON TIME : 9/5/2021 9:29 PM
USERNAME : susan
SESSIONNAME :
ID : 12
STATE : Disc
IDLE TIME : .
LOGON TIME : 9/6/2021 11:14 AM
Next, we need to do some house cleaning. Sometimes the Idle time will show up as a dot. We don't want that. We want it to show up as null. We do this by finding all the dot Idle times and setting that idle time to null with a foreach-object.
USERNAME : david
SESSIONNAME : console
ID : 11
STATE : Active
IDLE TIME : 1:08
LOGON TIME : 9/5/2021 9:29 PM
USERNAME : susan
SESSIONNAME :
ID : 12
STATE : Disc
IDLE TIME :
LOGON TIME : 9/6/2021 11:14 AM
Finally, we output the information by just putting the $users. That's it yall. Lets put it together.
Another way to do this is to grab a process from the machine that everyone logged in would be using. Something like explorer. We do this with the get-ciminstance and the wmi object win32_process.
Recently the send-mailmessage was put to rest with good reason. It failed to do its job by securing the emails. It sent items via plain text and not SSL encrypted. Great for internal nothing fancy, but bad if you wanted to send data outside the world. So, I built an alternative that I use the mail message system inside windows. Let's Send Mail With Powershell.
The Script
function Send-SHDMailMessage {
<#
.SYNOPSIS
Sends an email using custom SMTP settings.
.DESCRIPTION
Sends an email using custom SMTP settings.
.PARAMETER From
As string. Name of the Email address from which it will come from.
Example: "David Bolding <admin@example.com>"
.PARAMETER To
As string. The email address too.
Example: "Admin@example.com"
.PARAMETER SMTPUsername
As string. The Username for the SMTP service you will be using.
.PARAMETER SMTPPassword
As string. The Password in plain text for the smtp service you will be using.
.PARAMETER SMTPServer
As string. The server name of the SMTP service you will be using.
.PARAMETER SMTPPort
As string. The Server Port for the smtp serivce you will be using.
.PARAMETER Subject
As string. The subject line of the email.
.PARAMETER Body
As string. The body of the email as a string. Body takes default over BodyAsHtml
.PARAMETER BodyAsHTML
As string. The body of the email in html format.
.PARAMETER PlainText
Sends the email in plain text and not ssl.
.PARAMETER Attachment
As array of strings. A list of full file names for attachments. Example: "c:\temp\log.log"
.PARAMETER CC
Email address for a carbon copy to this email.
.PARAMETER BCC
Email address for a blind carbon copy of this email.
.PARAMETER Priority
A validate set for High, Normal, Low. By default it will send emails out with Normal.
.EXAMPLE
$Message = @{
from = "HR <HumanResources@Example.com>"
To = "Somebody@Example.com"
SMTPUsername = "SMTP2GoUsername"
SMTPPassword = "SMTP2GoPassword"
SMTPServer = "mail.smtp2go.com"
SMTPPort = "2525"
Subject = "Test"
Attachment = "C:\temp\JobOffer1.pdf","C:\temp\JobOffer2.pdf"
BodyAsHtml = @"
<html>
<body>
<center><h1>Congradulation</h1></center>
<hr>
<p>Attached is the job offers we discussed on the phone.</p>
<br>
Thank you,<br><br>
Human Resources
</body>
</html>
"@
}
Send-SHDMail @Message
Sends an email using the required information with two attachments.
.EXAMPLE
$Message = @{
from = "HR <HumanResources@Example.com>"
To = "Somebody@Example.com"
SMTPUsername = "SMTP2GoUsername"
SMTPPassword = "SMTP2GoPassword"
SMTPServer = "mail.smtp2go.com"
SMTPPort = "2525"
Subject = "Test"
BodyAsHtml = @"
<html>
<body>
<center><h1>Sorry, Not Sorry</h1></center>
<hr>
<p>Sorry you didn't get the job. Maybe next time show up with clothing on.</p>
<br>
Thank you,<br><br>
Human Resources
</body>
</html>
"@
}
Send-SHDMail @Message
This will send out an email without any attachments.
.EXAMPLE
$Message = @{
from = "HR <HumanResources@Example.com>"
To = "Somebody@Example.com"
SMTPUsername = "SMTP2GoUsername"
SMTPPassword = "SMTP2GoPassword"
SMTPServer = "mail.smtp2go.com"
SMTPPort = "2525"
Subject = "Test"
Body = "Your Hired"
}
Send-SHDMail @Message
Sends out a message using just a simple text in the body.
.EXAMPLE
$Message = @{
from = "HR <HumanResources@Example.com>"
To = "Somebody@Example.com"
SMTPUsername = "SMTP2GoUsername"
SMTPPassword = "SMTP2GoPassword"
SMTPServer = "mail.smtp2go.com"
SMTPPort = "2525"
Subject = "Test"
Attachment = "C:\temp\JobOffer1.pdf","C:\temp\JobOffer2.pdf"
}
Send-SHDMail @Message
This will send out an email that is blank with attached items.
.EXAMPLE
$Message = @{
from = "Notify <Notify@Example.com>"
To = "Somebody@Example.com"
SMTPUsername = "SMTP2GoUsername"
SMTPPassword = "SMTP2GoPassword"
SMTPServer = "mail.smtp2go.com"
SMTPPort = "2525"
Subject = "$SomethingWrong"
PlainText = $true
}
Send-SHDMail @Message
This will send out an unsecured email in plain text.
.EXAMPLE
$Message = @{
from = "Notifiy <Notify@example.com>"
To = "IT@example.com"
SMTPUsername = "SMTPUser"
SMTPPassword = "SMTPPassword"
SMTPServer = "mail.example.com"
SMTPPort = "2525"
Subject = "Server Down"
CC = "ITManagers@Example.com"
BCC = "CFO@Example.com"
PlainText = $True
Priority = "High"
BodyAsHTML = @"
<html>
<body>
<center><h1>SERVER DOWN!</h1></center>
</body>
</html>
"@
}
Send-SHDMailMessage @Message
.OUTPUTS
no Output.
.NOTES
Author: David Bolding
Date: 09/8/2021
.LINK
#>
[cmdletbinding()]
param (
[parameter(Mandatory = $true)][String]$From,
[parameter(Mandatory = $true)][String]$To,
[parameter(Mandatory = $true)][String]$SMTPUsername,
[parameter(Mandatory = $true)][String]$SMTPPassword,
[parameter(Mandatory = $true)][String]$SMTPServer,
[parameter(Mandatory = $true)][String]$SMTPPort,
[parameter(Mandatory = $true)][String]$Subject,
[Switch]$PlainText,
[string]$Body,
[String]$BodyAsHTML,
[String[]]$Attachment,
[string]$CC,
[string]$BCC,
[Validateset("High","Low","Normal")][String]$Priority
)
# Server Info
$SmtpServer = $SmtpServer
$SmtpPort = $SmtpPort
# Creates the message object
$Message = New-Object System.Net.Mail.MailMessage $From, $To
If ($PSBoundParameters.ContainsKey("CC")) {
$Message.CC.Add($CC)
}
If ($PSBoundParameters.ContainsKey("BCC")) {
$Message.Bcc.Add($BCC)
}
If ($PSBoundParameters.ContainsKey("Priority")) {
$Message.Priority = $Priority
} else {
$Message.Priority = "Normal"
}
# Builds the message parts
$Message.Subject = $Subject
if ($PSBoundParameters.ContainsKey("Body")) {
$Message.IsBodyHTML = $false
$Message.Body = $Body
}
elseif ($PSBoundParameters.ContainsKey("BodyAsHTML")) {
$Message.IsBodyHTML = $true
$Message.Body = $BodyAsHTML
}
else {
$Message.IsBodyHTML = $false
$Message.Body = ""
}
if ($PSBoundParameters.ContainsKey('Attachment')) {
foreach ($attach in $Attachment) {
$message.Attachments.Add("$Attach")
}
}
# Construct the SMTP client object, credentials, and send
$Smtp = New-Object Net.Mail.SmtpClient($SmtpServer, $SmtpPort)
if ($PlainText) {
$Smtp.EnableSsl = $true
}
else {
$Smtp.EnableSsl = $true
}
$Smtp.Credentials = New-Object System.Net.NetworkCredential($SMTPUsername, $SMTPPassword)
$Smtp.Send($Message)
#Closes the message object and the smtp object.
$message.Dispose()
$Smtp.Dispose()
}
Examples
$Message = @{
from = "HR <HumanResources@Example.com>"
To = "Somebody@Example.com"
SMTPUsername = "SMTP2GoUsername"
SMTPPassword = "SMTP2GoPassword"
SMTPServer = "mail.smtp2go.com"
SMTPPort = "2525"
Subject = "Job Offers"
Attachment = "C:\temp\JobOffer1.pdf","C:\temp\JobOffer2.pdf"
BodyAsHtml = @"
<html>
<body>
<center><h1>Congradulation</h1></center>
<hr>
<p>Attached is the job offers we discussed on the phone.</p>
<br>
Thank you,<br><br>
Human Resources
</body>
</html>
"@
}
Send-SHDMail @Message
In this example, I am using the SMTP2go service to send a job offer letter to a new employee. It contains the attachment flag with two offers. Each attachment is separated with a comma as this is a list of strings. The Body is an HTML using the BodyAsHTML flag. The BodyAsHTML has some custom formatting to make it look somewhat nice.
$Message = @{
from = "HR <HumanResources@Example.com>"
To = "Somebody@Example.com"
SMTPUsername = "SMTP2GoUsername"
SMTPPassword = "SMTP2GoPassword"
SMTPServer = "mail.smtp2go.com"
SMTPPort = "2525"
Subject = "Thank you"
BodyAsHtml = @"
<html>
<body>
<center><h1>Sorry, Not Sorry</h1></center>
<hr>
<p>Sorry you didn't get the job. Maybe next time show up with clothing on.</p>
<br>
Thank you,<br><br>
Human Resources
</body>
</html>
"@
}
Send-SHDMail @Message
In this example, we are once again using the SMTP2Go to send a rejection letter. No attachments are present on this email. The bodyashtml string is set with a nice custom HTML page.
$Message = @{
from = "Notify <Notify@Example.com>"
To = "Somebody@Example.com"
SMTPUsername = "SMTP2GoUsername"
SMTPPassword = "SMTP2GoPassword"
SMTPServer = "mail.smtp2go.com"
SMTPPort = "2525"
Subject = "Server Down"
Body = "XYZ Server Is Down"
}
Send-SHDMail @Message
In this example, we are sending out a notification email using a different user than before. We are using the same smtp2go, but you can use any server with the username and password you like. The body is a basic string with no HTML formating.
In this example, we are sending a notification email without a body. We are using a custom variable for the subject line. We are also sending this without the SSL encryption as some legacy systems don't understand SSL encryption.
In this example, we are Sending a Carbon copy to the IT manager and a blind carbon copy of the email to the CFO with a high priority.
Notes
The Body and BodyAsHTML can conflict with each other. If you do both the Body and BodyAsHTML, by default, the body will be selected. If you do not put in a body or bodyashtml, it will send the email with a body of "".
This script requires you to have an SMTP service like SMTP2Go.
Conclusion
That my friends is how you Send Mail With Powershell.
We use cookies to ensure that we give you the best experience on our website. If you continue to use this site we will assume that you are happy with it.