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.
In the last blog, I talked about how to upload to your next cloud’s file drop, aka upload only, password-protected folder. This time we will go over how to download from a password-protected folder. Let’s go over how to share out a file with a secure password.
Password-Locked Single File
Navigate to the file you wish to share out.
Click on the file in question.
Click the share link + symbol.
Checkbox the “Password Protect”
Enter a new secure password.
Document the password!
Click the Arrow to set the password
click the Clipboard to copy the link.
In another browser, navigate to the link to make sure it works.
Now we have the File shared, it’s time to download it.
The share ID
The Share Password
The URL of the nextcloud site.
The file name/location of where you will save the file.
Now you have those pieces of information, you can start the process of creating the header. Let’s look at the Variables first.
The Authorization type is going to be basic. We are going to convert the shareid and the sharepassword into the UTF8 so our nextcloud can understand it. We want all that as a base 64. So we create the string with “$($ShareID):$($SharePassword)” and push that into our System.Text.Encoding UTF8. Using the method GetByes. All that is then put into the System.Convert base of 64 string, aka password.
Next, we tell the site what we are requesting, we are requesting the XML HTTP request. Next, we will create the URL that will be used by the rest method.
$URL = "$($NextCloudURL)public.php/webdav"
Now we create the invoke-restmethod to download the file in question.
Now that we covered password locked, it’s time to look at the non-password locked. This is a single line item. Like above, just don’t create a password. Copy the URL.
Navigate to the file that you wish to share
clickt he share icon
Click the share link +
Copy the Link
This is a single-line command. The copied item will be the URL/shareID. If you place a /download at the end, it will download accordingly. Nice right. Its even nicer with PowerShell.
That’s it! A simple invoke-webrequest. Now, let’s take it to a different level.
Download a single file from a shared Folder
If you share out a single folder, you can download the files directly from that folder if you know those file names, even if the file isn’t shared itself. Standard information is required. First, let’s make the shared folder.
Navigate to the folder you wish to share out.
Click the share icon.
Click the Share Link +
Click the 3 Dots.
Click the Password Protect
Enter a new password
Document the new password
Click the Arrow beside the password
Click the clip board to copy the link
Here are the items you are going to need to setup this command.
The URL of the next cloud
The Share ID
The Share Password
The File name you are going to download.
The filename/location where you will be downloading too.
The share this time is the folder. So we are authing to that folder. This is great because if you forget the password later, it’s in the script. Next, we grab the file using the invoke-restmethod.
Invoke-RestMethod -Method GET -Header $Header -uri "$($NextCloudURL)public.php/webdav/$($Filename)" -OutFile $Outfilepath
Notice at the end of the URL, the $($filename). This is the filename inside the share. So we are looking for that file to download. That’s all, take a look at the script.
I love my nextcloud. It is hosted at my house, and I can upload files from anywhere in the world. It’s very clean, and one of the most awesome features is the file drop. I use scripts to upload the results all the time to the file drop location on my next cloud. A file drop is an upload-only file location. This means the outside world can not see the files inside of it. Only I can do that. I can also password lock this file drop location so only those with the password can get into it. It’s pretty cool.
How to setup a File Drop
I am going to assume you have a nextcloud. Once you log in, click the + icon to create a folder. We will call it PFD for password file drop and click the arrow to create.
Now we will set up the sharing side with the password lock.
Click the PFD folder to open the menu on the right hand side.
Click the sharing icon
Click the share link +.
Click the three dots
Radio check the File Drop (Upload Only)
Check the Password Protect
It will auto generate a password, It’s best to make your own.
Document your password!
Click the arrow to save the password
Click the clip board to copy the link for your new share
In a different browser test your link.
Now you have a safe place for the files to go. It’s time to upload files via PowerShell. You will need a few items. First the URL of your site. example https:\\cloud.bolding.us. Next, you will need the shareID of the folder in question. That can be found in your URL at the end. Example
The URL. For this example we will be using https://cloud.bolding.us
The shareID of the directory in question. It is the last part of the main url that we copied by clicking the clip board. I have bolded it for you in this example: https://cloud.bolding.us/index.php/s/oWHeW4dfWnxwXXX
Next you will need the password.
Those are the three things you will need to create your invoke-restmethod. Lets build the script. Lets declare our variables.
Now the hard part. We need to create the header. This is where we will be placing the passwords and the type of information we are going to be accessing.
The Authorization type is going to be basic. We are going to convert the shareid and the sharepassword into the UTF8 so our nextcloud can understand it. We want all that as a base 64. So we create the string with “$($ShareID):$($SharePassword)” and push that into our System.Text.Encoding UTF8. Using the method GetByes. All that is then put into the System.Convert base of 64 string, aka password.
Next, we tell the site what we are requesting, we are requesting the XML HTTP request. Next, we will create the URL that will be used by the rest method.
Now we have the Header that will be needed for our rest method. We have the URL. now we need the method and the file. We do the file by using the -InFile and select the full name of the file $Item.Fullname. The method will be PUT as we are putting something somewhere.
Invoke-RestMethod -Uri $URLBuild -InFile $Item.Fullname -Headers $Headers -Method Put
Now it’s time to put it all together into a workable function.
WSD is an awesome service for printers. It goes out and finds a printer on the network and adds it accordingly. It does all the IP address stuff for you. Which is awesome. It even tells you that it was set up as a wsd by naming the port wsd and some code. Super friendly. However… What happens when DHCP changes that IP address because someone forgot to do a reservation? How do you get that IP address? Believe it or not, that IP address is stored in the registry for the most part. Its located under the Hardware Local Machine > System > Current Control Set > Enum > SWD > DAFWSDProvider. Each entry has a friendly name and location information. The location information has the IP address like a web page. Powershell can give you this information pretty quickly with a single line as well. Let’s take a look.
Get-childitem is normally used in directories. The registry is a type of directory with files inside of it. So we use the get-childitem and treat the registry as a file path. We navigate to the DAFWSDProvider. Then we look at each file or in this case item property with Get-ItemProperty. We are looking for that FriendlyName and the LocationInformation. The location information will look like https://192.168.2.62/something because it is treating the IP address as a webpage. Still unsure why. The above command will list all of the printers like this. If it has an IP address, it will appear there.
Now we can wrap this up in a nice little function for remote computers.