Install Perch with PowerShell

Install Perch with PowerShell

Perch is an event log tracker that can catch a lot of useful information. I like perch because it captures failed login information. It’s easy to sort and exportable. This is why many companies use the software. There are some gatchya’s with perch installs though. If you are installing it on a server, some services don’t auto start. Installing it from PowerShell also has a gatchya. This post is about how to install perch via PowerShell scripts. This method uses your token for the client’s site. Let’s Install Perch with PowerShell.

The Script

if (!(Test-Path "$($env:SystemDrive)\Temp")) { New-Item -Path "$env:SystemDrive\" -Name Temp -ItemType Directory }
$PerchURL = "https://cdn.perchsecurity.com/downloads/perch-log-shipper-latest.exe"
$PerchFullFileName = "$($env:SystemDrive)\Temp\perch-log-shipper-latest.exe"
Invoke-WebRequest -Uri $PerchURL -Outfile $PerchFullFileName -UseBasicParsing
start-process -FilePath "$PerchFullFileName" -ArgumentList @("/qn", 'OUTPUT="TOKEN"', 'VALUE="Your Token"') 
$Timeout = 0
do {
    $Timeout = $Timeout + 5
    start-sleep -Seconds 5
} Until (((Get-service -Name "Perch*" -ErrorAction SilentlyContinue).count -ge 2) -or ($Timeout -ge 500))

if ((Get-service -Name "Perch*" -ErrorAction SilentlyContinue).count -ge 2) {
    Get-Service -name perch-auditbeat | Set-Service -StartupType Automatic
    Get-Service -name perch-auditbeat | Start-Service
} else {
    Write-Error "Services did not install."
}

The Breakdown

Let’s break down the script. The first thing we do is create the download repo. I personally like to use the C:\Temp. Not all machines have a c:\. This is why I use the variable $Env:SystemDrive. If the OS drive is d, the code will add a D:\Temp. and so on and so forth.

The next line is the URL for the latest and greatest Perch installer. This keeps your download up to date. With that stated, this also means if they change something you will need to be able to catch that change. So you will need to stay up to date with their deployment. A good way to do that is by registering with their updates emails. I like to have a ticket every 3 to 6 months, randomly placed, to review deployments like this one. This is just a good habit.

Now we have the url, we want to create a path. Using the Env:SystemDrive we place the perch-log-shipper-latest.exe into the temp folder, our local repo. This will make the next command easier.

Now we invoke-webrequest this bad boy. Just like curl and wget, we are able to download the file. Using the PerchURL in the URI position and then the outfile will be the perchfullfilename. Of course, we use the basic parsing just in case it’s an older version of PowerShell. At the time of this writing, the default PowerShell is 5.1 on windows 10.

Now we start the installation. We start-process. Using the PerchFullFileName as the target. See, using parameters helps. Our argument list is /qn for quiet. The output is going to be the token. Finally our token value, value is our token from perch’s site.

Getting the token

To get the token, you will need to log into your perch system. At the top, select the company you wish to get the token from.

Next, you will need to click on the settings icon on the bottom left-hand corner. Then click the Network icon.

Normally we want to add a -wait flag to the end of the installer. Things like google chrome do great with the -wait flag. However, in this case, we don’t want to do that. The reason we create advanced checks is due to the multiple sub-processes inside of the perch install process. The wait flag only captures the first process.

Confirming Install Perch with Powershell

With all that, it’s time to confirm the installation. The most simple way to do this is by watching the services. Perch installs 2 services. It installs perch-auditbeat and perch-winlogbeat. During the confirmation process, we wait. If a timeout occurs, we get the error. If the application installs, we get the results. At this point, we want to start our time-out timer. That’s why we have a $timeout = 0. We then start a do until. Each time the system loops, we wait for 5 seconds. Then we add 5 to the timer. This effectively creates a timer for us. This process is repeated until the conditions are met. The services with the name perch* are greater than or equal to 2, or the time-out reaches 500.

If the services are installed or if the timeout is reached, we moved to the next step. By default, the auditbeat is set to manual. So we check to see if we have the two services. If we do, we then set the perch-auditbeat to automatic and start the service. If not, we throw an error saying the services did not install. This will alert the deployment engineer to dig deeper into this machine. From my experience, it’s because another application is installed.

Don’t forget to take a look at how you can install perch using intune.

Powershell HTML Reports

Powershell HTML Reports

From time to time, you will be asked to create a web report with data that can only be easily obtained by PowerShell. There are hundreds of modules out there that can do this, which is very nice. This blog isn’t about those modules, but instead some tricks I have learned over the years. When it comes to custom HTML reports, these are very customized from data sources like Azure, Active Directory, even SLQlite services. If all possible I like to use the Universal dashboard. But the UD doesn’t translate to emails or IIS pages very well. Let’s get started with the here string.

Here-String

$HereString =@"
Information
"@

A here-string is used to create a block of text. In the example above you see the variable HereString is equal to @””@. This is very important. The @ symbol indicates the start and stops of a here-string. You can place variables inside the here-string as well. This means you can build variables outside of the here-string and plump them into the string. Here is an example of our base for the website.

$HTML = @"
<html>
<head>
<title>Employee List</title>
<style>
</style>
</head>
<body>
</body>
</html>
"@

From this template, we can add CSS coding, powershell tables, lists and much more. The next important item we need to talk about is the convertto-html commandlet.

Convertto-html

This command is the heart of creating tables out of PowerShell information. For example, you want to pull all enabled users from the employees OU and display their displaynames, samaccountnames, department, and title in order. That’s a simple get-aduser command.

$users = Get-ADUser -Filter {enabled -eq $true} `
-SearchBase "OU=hospice Users,DC=Hospice,DC=Local" `
-Properties displayname,samaccountname,title,department `
| Select-Object Displayname,Samaccountname,Title,Department `
| sort-object Displayname

Now we need to convert this array of data into a useable table. This is where convertto-html comes into place. We can tell the command to create a full website just for the tabled information or we can tell it to produce a single table using the as flag and the fragment flag.

$UsersToHTML = $users | ConvertTo-Html -as Table -Fragment

Now we have a user table that we can drop into our Here-string from above. The Here-string is the last thing you will create during this custom process.

$HTML = @"
<html>
<head>
<title>Employee List</title>
<style>
</style>
</head>
<body>
$UsersToHTML
</body>
</html>
"@

This is pretty much the basics of how you would create a site of all users and their user names. You export that site using the >> command. So it would look like this:

$HTML >> <Pathtohtml>.html

Going Deeper – Employee Directory With Images

Let’s go a little deeper by making an employee Directory with pictures. In this scenario, we will assume that the active directory has thumbnail photos inside of it. This Scenario is going to be based on an IIS server and everything is set up for a basic HTML page. I will as

Requirements:

  • If a property is not present in the AD account, null or blank, do not display that property.
  • All users must have a photo or a placeholder.
  • The table must be in alphbetical order by the displayname.
  • Required Properities:
    • Photo
    • Display Name
    • Username
    • Job Title
    • Department
    • Mobile Phone
    • Office Phone
    • Fax
    • Manager

The first thing we need to do is grab the data that we need from the active directory. We do that with the get-aduser command.

$users = Get-ADUser -filter {enabled -eq $true} `
-Properties thumbnailPhoto,Displayname,SamAccountName,Title,Department,MobilePhone,OfficePhone,Fax,Manager

There will be two tables that we will be working with. The first table will be two columns and however many rows we will need. The first column will contain the 128×128 active directory thumbnail photo. The second column will contain the Users information. The second column will contain a table with two columns and 8 rows. The first column will be the name of the value and the second column will be the value itself.

I can do this in two ways. I can start the first table inside the here-string or I can create the table before the here-string. I’m going to create the table before the here-string. This way I have a clear image in my head of the table that I am inputting into the here-string. Notice the UserTable is a here-string as well. So we will refer to the Main HTML here-string as the HTML string from here on out.

$UserTable = @"
<table id="myTable">
  <tr class="header">
    <th style="width:20%;visibility: hidden;"></th>
    <th style="width:30%;visibility: hidden;"></th>
  </tr>
"@

Now we have a basic HTML table built. From here on out, we will be creating the rows for this table. Inside each row will be the two columns. The photo column and the data column. The data column will contain the table with the employee data on it. It’s time to start looping through that user’s object we created a while ago. We start off with a simple foreach loop. Inside the loop the logic will go as follows:

  • Grab the photo from the thumbnail property and save it to the image repo using the username.
  • Start the new row. Inside the first column of the new row, we check to see if that photo exists. If it does, then we we create an img tag of the file, if it doesn’t then we target the general image file.
  • The second column we blank out the values for each item.
  • Then we create the table.
  • Then we create if statements that creates each row in table 2. if the users object has the property in question for that user, we set the value and create the <tr> accordingly.
  • Finally we close off the second table and the row.
  • Then after all of the rows on table 1 is created, we close off the table.

Phew. This is going to be crazy.

The first part is to download the photo in question. If the user’s profile has a thumbnail photo we will download it from AD using the set-content command. (PS5)

foreach ($User in $Users) {
    $PhotoPath = "<path>/images/$($user.samaccountname).jpg"
    if ($Null -ne $user.thumbnailPhoto) {
        $user.thumbnailPhoto | Set-Content $PhotoPath -Encoding byte
    }
}

The next step is to determine if the file exists. If it does, we want to create the Image URL towards the new user’s image. If not, we want it to go to the placeholder image. All of this is still within the Loop in question.

if (Test-Path $PhotoPath) { 
    $ImageURL = "Images/$($user.UserPrincipalName).jpg" 
} else { 
    $ImageURL = "Images/Placeholder.png" 
}

Now we need to make a placeholder for each property that we will be using. We do this so the next time the loop processes, we have fresh variables.

$DisplayName = ""
$Username = ""
$JobTitle = ""
$Department = ""
$MobilePhone = ""
$OfficePhone = ""
$Fax = ""
$Manager = ""

Now we need to test these variables and fill them up with HTML code. Each one of these will be a row on the table2. The code below is the same for each of these. All you need to do is replace the <value> with the value in question. The one on the left is what you will replace. The one on the right is an example.

if ($Null -ne $User.<Value>) {
    $<value>= @"
    <tr>
      <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong><value>:</strong>
      </td>
      <td style='vertical-align: top; text-align:left;'>
        $($User.<value>)
      </td>
    </tr>
"@
  } 
if ($Null -ne $User.OfficePhone) {
    $OfficePhone= @"
    <tr>
      <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Office Phone:</strong>
      </td>
      <td style='vertical-align: top; text-align:left;'>
        $($User.OfficePhone)
      </td>
    </tr>
"@
  } 

Once you create a value for each of these items, it’s time to put it together into the User’s table. AKA table2. We start off by using the $usertable from above combined with a here-string. Remember we are now entering the first table again and creating each row. Here is what the code will look like.

  $UserTable = $UserTable + @"
    <tr>
        <td> <center><a href="$ImageURL" target="_blank"><img src='$ImageURL' style='width:125px; height:125px; border-radius:100px;'></a></center></td>
        <td>
            <table>
                $DisplayName
                $UserName
                $JobTitle
                $Department
                $MobilePhone
                $OfficePhone
                $Fax
                $Manager
            </table>
        </td>
    </tr>
"@

We are building this string. This is what you see the $usertable = $usertable + Here-String. All of this is the single row for the table in question. We loop through all of the users using this method and close out the Loop. Once the loop is closed out what we need to do next is close out the table. We do this by adding a final </table> to the end of the table in question.

$UserTable = $UserTable + "</table>"

The final part is placing this table inside an HTML website. It’s as simple as slipping $UserTable between the two bodies.

$HTML = @"
<html>
<head>
<title>Employee List</title>
<style>
</style>
</head>
<body>
$UserTable
</body>
</html>
"@

Now we have an HTML site. We can further edit this and create a nice CSS code with some java-scripts, but I’m not going to get fancy here. The final thing we have to do is export this html page.

$HTML > <pathtosite>\index.html

This process is basically building inside out. We started by the image and then the data. We built accordingly. Now the script itself.

The Script

param (
    $Filepath = (Read-Host "File Path for "),
    $ImagePath = (Read-Host "Image Folder")
)
$users = Get-ADUser -filter {enabled -eq $true} `
-Properties thumbnailPhoto,Displayname,SamAccountName,Title,Department,MobilePhone,OfficePhone,Fax,Manager

$UserTable = @"
<table id="myTable">
  <tr class="header">
    <th style="width:20%;visibility: hidden;"></th>
    <th style="width:30%;visibility: hidden;"></th>
  </tr>
"@
foreach ($User in $Users) {
    $PhotoPath = "$ImagePath/$($user.samaccountname).jpg"
    if ($Null -ne $user.thumbnailPhoto) {
        $user.thumbnailPhoto | Set-Content $PhotoPath -Encoding byte
    }
    if (Test-Path $PhotoPath) { 
        $ImageURL = "Images/$($user.UserPrincipalName).jpg" 
    } else { 
        $ImageURL = "Images/Placeholder.png" 
    }
    $DisplayName = ""
    $Username = ""
    $JobTitle = ""
    $Department = ""
    $MobilePhone = ""
    $OfficePhone = ""
    $Fax = ""
    $Manager = ""
    if ($Null -ne $User.displayname) {
        $DisplayName= @"
    <tr>
        <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Display Name:</strong>
        </td>
        <td style='vertical-align: top; text-align:left;'>
        $($User.displayname)
        </td>
    </tr>
"@
    } 
    if ($Null -ne $User.samaccountname) {
        $Username= @"
    <tr>
        <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Username:</strong>
        </td>
        <td style='vertical-align: top; text-align:left;'>
        $($User.samaccountname)
        </td>
    </tr>
"@
    } 
    if ($Null -ne $User.title) {
        $JobTitle = @"
    <tr>
        <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Job Title:</strong>
        </td>
        <td style='vertical-align: top; text-align:left;'>
        $($User.title)
        </td>
    </tr>
"@
    } 
    if ($Null -ne $User.Department) {
        $Department= @"
    <tr>
        <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Department:</strong>
        </td>
        <td style='vertical-align: top; text-align:left;'>
        $($User.Department)
        </td>
    </tr>
"@
    } 
    if ($Null -ne $User.MobilePhone) {
        $MobilePhone = @"
    <tr>
        <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Mobile Phone:</strong>
        </td>
        <td style='vertical-align: top; text-align:left;'>
        $($User.MobilePhone)
        </td>
    </tr>
"@
    } 
    if ($Null -ne $User.OfficePhone) {
        $OfficePhone= @"
    <tr>
        <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Office Phone:</strong>
        </td>
        <td style='vertical-align: top; text-align:left;'>
        $($User.OfficePhone)
        </td>
    </tr>
"@
    } 
    if ($Null -ne $User.Fax) {
        $Fax = @"
    <tr>
        <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Fax:</strong>
        </td>
        <td style='vertical-align: top; text-align:left;'>
        $($User.Fax)
        </td>
    </tr>
"@
    } 
    if ($Null -ne $User.Manager) {
        $ManagerInfo = $User.Manager.split(',')[0] -replace "CN=",''
        $Manager = @"
    <tr>
        <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Manager:</strong>
        </td>
        <td style='vertical-align: top; text-align:left;'>
        $($ManagerInfo)
        </td>
    </tr>
"@
    } 
    $UserTable = $UserTable + @"
    <tr>
        <td> <center><a href="$ImageURL" target="_blank"><img src='$ImageURL' style='width:125px; height:125px; border-radius:100px;'></a></center></td>
        <td>
            <table>
                $DisplayName
                $UserName
                $JobTitle
                $Department
                $MobilePhone
                $OfficePhone
                $Fax
                $Manager
            </table>
        </td>
    </tr>
"@
}
$UserTable = $UserTable + "</table>"
$HTML = @"
<html>
<head>
<title>Employee List</title>
<style>
</style>
</head>
<body>
$UserTable
</body>
</html>
"@
$Html > $Filepath

That’s all folk, yall have a great week!

Send-SHDMailMessage

Send-SHDMailMessage

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.

$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

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.

$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"
    Priority = "High" 
    BodyAsHTML = @"
            <html>
                <body>
                    <center><h1>SERVER DOWN!</h1></center>
                </body>
            </html>
        "@
}
Send-SHDMailMessage @Message

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.

Install Google Chrome with Powershell

Install Google Chrome with Powershell

My last weekly challenge to myself was to deploy google chrome with Powershell. This one was the hardest because it was hard to find the latest and greatest MSI 64 bit of google chrome. The coding wasn’t too hard after that. How google chrome works, follows suit the same way as firefox did.

The Script

$ChromeSource = "http://dl.google.com/tag/s/defaultbrowser/edgedl/chrome/install/GoogleChromeStandaloneEnterprise64.msi"
$Installer = "$ENV:TEMP\Google.msi"
Invoke-WebRequest -Uri $ChromeSource -OutFile $Installer
Get-Process -Name "Chrome" | Stop-Process -Force
msiexec /i $Installer /qn /norestart
Remove-Item $Installer

The Breakdown

We first start off getting the latest enterprise edition of google chrome. This is the 64-bit version thus the most stable version on them all. This is the version I would install everywhere in a company if I had a choice.

$ChromeSource = "http://dl.google.com/tag/s/defaultbrowser/edgedl/chrome/install/GoogleChromeStandaloneEnterprise64.msi"

Then we create the installer path and download the file using the Invoke-webrequest with the Outfile as the installer path.

$Installer = "$ENV:TEMP\Google.msi"
Invoke-WebRequest -Uri $ChromeSource -OutFile $Installer

Then we stop the google chrome process and install the latest version of google chrome using the msiexec command. Then we remove the installer.

Get-Process -Name "Chrome" | Stop-Process -Force
msiexec /i $Installer /qn /norestart
Remove-Item $Installer

That’s it. Very simple very straightforward. If you need to uninstall before installing (My testing was a success with overlapping the installers) then you can do the following before Installing google chrome:

$Chrome = Get-CimInstance win32_Product | where-object {$_.name -like "*Google*Chrome*"}
$Chrome | Invoke-CimMethod -MethodName Uninstall

Install Firefox with Powershell

Install Firefox with Powershell

Need to deploy the latest version of firefox to 1000 machines, Here is a little powerhouse script that can do just that. It’s similar to my last script (Gimp) as it downloads directly from the web. This time we don’t have to parse out a website since Modzilla has it laid out before us.

The Script

$FirefoxSource = "https://download.mozilla.org/?product=firefox-latest-ssl&os=win64&lang=en-US"
$Installer = "$ENV:TEMP\ModzillaFirefox.exe"
Invoke-WebRequest -Uri $FirefoxSource -OutFile $Installer
Get-Process -Name "*firefox*" | Stop-Process -Force
Start-Process -FilePath $Installer -ArgumentList "/s" -Verb runas -wait
Remove-Item $Installer

The Breakdown

The source is awesome. We are downloading directly from the site with their latest 64-bit product. This time we are going with the temporary file and then downloading the file with invoke-webrequest. Then we start the process of installing it with the /s flag which means silent. Since we are coming from the temp folder I threw in the runas flag to run it as the system. This way it installs for all users. Next, we set the wait flag to install the system. From everything I have read, we don’t need to uninstall the previous version to install the newest version. We do however have to stop the process that’s why we have a get process and stop process above. Finally, we remove the installer. That’s it. A lot simpler than Gimp.

Install Gimp with Powershell

Install Gimp with Powershell

This little script installs the latest version of gimp 2.10 onto your Windows machine. Let’s take a look at the script and then break it down.

The Script

$DownloadPath = "C:\Temp\Gimp"
If (!(Test-Path -Path "C:\Temp\")) {New-Item -Path c:\ -Name Temp -ItemType Directory }
If (!(Test-Path -Path "C:\Temp\Gimp\")) {New-Item -Path c:\Temp -Name Gimp -ItemType Directory }
$URL = "https://download.gimp.org/mirror/pub/gimp/v2.10/windows/"
$Gimp = Invoke-WebRequest -UseBasicParsing -Uri $URL -SessionVariable websession
$Links = $Gimp.Links | Where-Object {$_.href -like "*.exe"} | select-object -Last 1
$URLDownload = "$URL$($Links.href)"
$DownloadName = "$DownloadPath\Gimp.exe"
Invoke-WebRequest -Uri $URLDownload -OutFile $DownloadName
if (Test-path "C:\Program Files\GIMP 2") {
    Get-Process -Name "Gimp*" | Stop-Process
    Start-Process -FilePath "C:\Program Files\GIMP 2\uninst\unins000.exe" -ArgumentList "/VERYSILENT" -wait
}
Start-Process -FilePath $DownloadName -ArgumentList '/VERYSILENT /NORESTART /ALLUSERS' -wait
Remove-Item $DownloadName

The Breakdown

The first thing we do is set up the path we want to make. Then we test to see if the path exists. If they don’t, we make them. I’m using temp in this cause because I will be deploying this to 2000+ machines. We will remove the installer afterward. I want the Temp folder to existing afterward for future deployments.

$DownloadPath = "C:\Temp\Gimp"
If (!(Test-Path -Path "C:\Temp\")) {New-Item -Path c:\ -Name Temp -ItemType Directory }
If (!(Test-Path -Path "C:\Temp\Gimp\")) {New-Item -Path c:\Temp -Name Gimp -ItemType Directory }

Next, we grab the URL we want to work with This is the gimp’s official download portal. This portal is by default Oldest to newest when you pull from it using Powershell.

$URL = "https://download.gimp.org/mirror/pub/gimp/v2.10/windows/"

Then we use the Invoke-webrequest to grab the website as we did in a previous post. From there we grab all of the links. In this case, since it’s a repo, they are all download links except for 2. We only want the exes of the list, so we use a where-object to find those. Then we select the last 1 as it is the newest version.

$Gimp = Invoke-WebRequest -UseBasicParsing -Uri $URL -SessionVariable websession
$Links = $Gimp.Links | Where-Object {$_.href -like "*.exe"} | select-object -Last 1

Now we need to build our URL and our Path. This is some string controls. Notice the $($Something.Something) in this code. When you deal with an array in a string and want to grab a sub item, you need to call it out with the $().

$URLDownload = "$URL$($Links.href)"
$DownloadName = "$DownloadPath\Gimp.exe"

Next we download the Gimp 2.10 version we are wanting with another invoke-webrequest. This time we select the Outfile tab.

Invoke-WebRequest -Uri $URLDownload -OutFile $DownloadName

Now we want to uninstall the pervious version of Gimp. Since gimp doesn’t show up in the win32_products, we go to it manually in the file system. Newer gimps host themselves inside the program files > gimp 2. So we search to see if that folder exists with a test-path. If it does, we then check to see if gimp is running. Then kill it with fire… ok, not fire, but force. Gimp is awesome about putting an uninstaller inside the file system. So we will use that. It’s located in the Gimp 2 > Uninst > Unins000.exe. Which can be triggered with a /verysilent parameter to keep it quiet. We do this with a start process and we use a flag -wait to wait on it to uninstall.

if (Test-path "C:\Program Files\GIMP 2") {
    Get-Process -Name "Gimp*" | Stop-Process -Force
    Start-Process -FilePath "C:\Program Files\GIMP 2\uninst\unins000.exe" -ArgumentList "/VERYSILENT" -Wait
}

Then we start the install of the new gimp with the start-process again. We use the Download Name we made eailer with an argument list of /verysilent /norestart /allusers and a -wait.

Start-Process -FilePath $DownloadName -ArgumentList '/VERYSILENT /NORESTART /ALLUSERS' -Wait

Finally we remove the installer with a remote-item.

Remove-Item $DownloadName

That’s all it takes yall. I hope this is helpful to you.