Thursday, June 12, 2014

MS Orchestrator Custom HTML Emails

Sending email messages in Microsoft Orchestrator is simple using the built in activity, but... If you want to do something a little more personalized like sending as a no Primary SMTP address or a custom HTML message body?

... you need to use PowerShell to build the HTML and send the email.

Here is an example of a basic "Send Email" Runbook, all data required to customize and send the email is instantiated on the "Initialize" activity and passed to the .NET activity.


So in this Runbook, there is a .NET activity using PowerShell.

Here comes "Send-MailMessage", using this cmdlet we are able to build our email as we wish. The one issue with this cmdlet is that it uses authenticated SMTP connections. By default it will use the credentials of the user executing the command. In our case it is the Orchestrator service account.

The service account is mail enabled so in theory this isn't an issue however, you are only allowed to send as your "Primary" SMTP address listed in Exchange. I wanted to send messages as an alternate address based on the solution or Runbook that is leveraging the email activity I have created. So a work around is that you can specify alternate credentials for your SMTP connection. Yey!

However you need a relay connector in configured in Exchange (I am using Exchange 2010) that accepts autonomous connections!

Part 1: Custom SMTP 'From' address.

Here is the PowerShell code to send the email.

Try
{

$PSEmailServer = "relayserver.domain.com"
$from = "OrchestratorGod@domain.com"
$to = "user@domain.com"
$subject = "Email sent from Orchestrator God, Not my Primary SMTP"
$user = "anonymous"
$password = ConvertTo-SecureString –String "anonymous" –AsPlainText -Force
$creds = New-Object –TypeName System.Management.Automation.PSCredential –ArgumentList $user, $password
$body = "<head><b>Email Body</b></head>"


$Parameters = @{

to = $to
from = $from
subject = $subject
body = $body
credential = $creds
smtpserver = $PSEmailServer
bodyashtml = $True

}

Send-MailMessage @parameters

}#End Try
Catch
{

$Exception = $error[0].Exception.Message + "`nAt Line " + $error[0].InvocationInfo.ScriptLineNumber

}#End Catch

Part 2: Custom HTML Email Body

We need to expand the code to include a 'Replace' snipit to build the HTML string that will be embedded in the email. We will also need a HTML template so we can import a nice rich HTML email message to personalize.

Example of a HTML template:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <title></title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<style>
<!--
Style Here
-->
</style>
<body>
<p>To: <!@UserName@!></p>
<p>Date: <!@Date@!></p>
<h2>Your request for software is approved!</h2>
<h2>How to get your software?</h2>
<p>Please click <a href="<!@Link@!>">here</a> to go to the AppStore and select INSTALL for your application.</p>
<h2>Problems?</h2>
<p>If you are experiencing an issue with your application, please contact the <a href="mailto:servicedesk@domain.com">Service Desk</a> at Ext. 12-3456</p>
<h2>Feedback is welcome!</h2>
<p>Do let us know what you think – email comments and suggestions to <a href="mailto:automation@domain.com">Automation</a></p>
</body>
</html>

This template is saved as a file "template.html" to be used later when the runbook containing the email activity is run. This follows the principle that if you want to change the template you only have to change it in one place. However... If you have multiple Runbook servers this should be placed on a network share as all Runbook servers will require access, I would use a global variable in Orchestrator referencing the UNC path of the file.

Here is the updated PowerShell code to send the email with the new HTML body.

Try
{

$Template = "C:\temp\template.html"
$PSEmailServer = "relayserver.domain.com"
$from = "OrchestratorGod@domain.com"
$to = "user@domain.com"
$subject = "Email sent from Orchestrator God, Not my Primary SMTP"
$user = "anonymous"
$password = ConvertTo-SecureString –String "anonymous" –AsPlainText -Force
$creds = New-Object –TypeName System.Management.Automation.PSCredential –ArgumentList $user, $password
$UserName = "Harry Potter"
$Date = [DateTime]::Now.ToString("d")

#Replace HTML with Data
[string]$body = Get-Content $Template
$body = $body.Replace("<!@UserName@!>", $UserName)
$body = $body.Replace("<!@Date@!>", $Date)

$Parameters = @{

to = $to
from = $from
subject = $subject
body = $body
credential = $creds
smtpserver = $PSEmailServer
bodyashtml = $True

}

 Send-MailMessage @parameters

}
Catch
{

$Exception = $error[0].Exception.Message + "`nAt Line " + $error[0].InvocationInfo.ScriptLineNumber

}

Obviously these scripts are for example services, many of the variables would actually be populated from data on the "DataBus" or "Global Variables" from within Orchestrator and not statically assigned.

So here is it, a custom email sent from a Runbook in MS Orchestrator, using a SMTP address not associated to the service account's Exchange email account.

Next time... Embedding images :)

Hope this helps.

All information is provided on an AS-IS basis, with no warranties and confers no rights.

Monday, March 10, 2014

MS Orchestrator PowerShell Template


In MS Orchestrator 2012 we leverage PowerShell using the .NET activity a lot and we needed a template for all PowerShell to standardize our code and to provide a standard for exception errors.

So using the .NET activity this is the format I used.


Using this template any error exception in the PowerShell code is published to a variable $Exception.

We then publish this $Exception variable as an output string on the .NET activity.


Using the published variable $Exception we can change the workflow within the Orchestrator Runbook.

Test Runbook:


Using the above Runbook as a test we use the $Exception variable as a decision test for the links between the two outcome activities.

The "Exception Error" link will only be allowed to process if the $Exception variable does not match the regular expression ^$. This checks for NULL/Empty.


So if the variable is populated (there was an error) the condition passes to the next task which logs an error in the Platform Event. We then pass the information about the Activity, Error Summary Text and finally the $Exception variable to the event log.


Alternatively if the $Exception variable is NULL/Empty then the Link[Exception Error] does not process but the other Link[Success] does as it only allows an $Exception state of NULL/Empty.

So there you have it, a basic template for PowerShell .NET activity that allows a robust exception detection for your code.

Hope this helps.

All information is provided on an AS-IS basis, with no warranties and confers no rights.

Wednesday, October 2, 2013

Bored = Stupid Tool

Waiting for your AD account to unlock?
The other day I was waiting for my account to unlock, so I had a few minutes to kill so I decided to write a tool to pass the time.

I wrote a script to tell me how long I had left to wait... pretty crude but I had fun.

Code:

Function Get-LockoutRemainingTime
{
Param($ID)
    Try
    {
        $Account = Get-ADUser $ID -Properties LockedOut,AccountLockoutTime
        $Domain = [ADSI]"WinNT://$env:userdomain"
        $UnlockInt = $Domain.AutoUnlockInterval
            If($Account.LockedOut -eq $TRUE)
            {
                Write-Host -BackgroundColor DarkRed "Account $($Account.Name) is locked out!"
                [Int32]$i = ($Account.AccountLockoutTime.AddSeconds(1800) | %{New-TimeSpan $(Get-Date) $_}).TotalSeconds
                Do{Write-Host "Account Unlocked in $i seconds";$i--;start-sleep -s 1}While($i -ne 0)
            }
            Else
            {
                Write-Host -BackgroundColor DarkGreen "Account $($Account.Name) is not locked out!"
            }
    }
    Catch
    {
        Write-Host "Script Failure - $($_.Exception.Message)"
    }
}#End Function
Get-LockoutRemainingTime -ID "CN or DN"

Hope this helps.

All information is provided on an AS-IS basis, with no warranties and confers no rights.

Thursday, August 22, 2013

Commenting Code in ISE

One of my biggest frustrations with Powershell ISE V3 is there is no "Comment" feature which for an IDE(ISE) , well lets just say I am disappointed.

Yesterday I stumbled across this post and this guy has a neat way of achieving this without having to add a plug in.

Posted by Joel 'Jaykul' Bennett on 1/25/2012 at 6:39 PM
position the cursor at the left edge of the topmost line to be commented out, HOLD alt + shift, press "DOWN ARROW" once for each line you want to comment out, then press "#"

To uncomment, do the same thing except hit DEL instead of "#" ;-)
So I hope this helps some of you out there, it did me!

Wednesday, August 14, 2013

Adding Windows Features - AD Tools

Quick and Dirty one...

open up Powershell "As Administrator"

Type...

C:\> Import-Module ServerManager
C:\> Add-WindowsFeature "RSAT-AD-Tool" -Reboot

This will install the Active-Directory management GUI and Powershell CMDlet tools.

PS: Will reboot after it installs the feature!!!

Hope this helps

Wednesday, July 24, 2013

Powershell Profiles

I needed to validate what Powershell profiles existed on my machine so I used the "Test-Path" CMDLet to validate what existed.

Profiles:

DescriptionPath
Current User, Current Host - console
$Home\[My ]Documents\WindowsPowerShell\Profile.ps1
Current User, All Hosts
$Home\[My ]Documents\Profile.ps1
All Users, Current Host - console
$PsHome\Microsoft.PowerShell_profile.ps1
All Users, All Hosts
$PsHome\Profile.ps1
Current user, Current Host - ISE
$Home\[My ]Documents\WindowsPowerShell\Microsoft.P owerShellISE_profile.ps1
All users, Current Host - ISE
$PsHome\Microsoft.PowerShellISE_profile.ps1

PS C:\> test-path $PROFILE
True
PS C:\> test-path $PROFILE.CurrentUserCurrentHost
False
PS C:\> test-path $PROFILE.AllUsersAllHosts
True
PS C:\> test-path $PROFILE.AllUsersCurrentHost
False
PS C:\> test-path $PROFILE.CurrentUserAllHosts
False
PS C:\>

Then to find the file I just typed in the profile name.

PS C:\> $PROFILE.CurrentUserCurrentHost
C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1
PS C:\>

Note: The profile file must be edited with administrative permisions.

Hope this helps.

Powershell W2008R2 - Adding Features

A quick trick to understand what features you can add with the Server-Manager module in Powershell.

Open a Powershell command line (As Administrator),

C:\> Import-Module ServerManager
C:\>Get-WindowsFeature

Display Name                                            Name
------------                                            ----
[ ] Active Directory Certificate Services               AD-Certificate
    [ ] Certification Authority                         ADCS-Cert-Authority
    [ ] Certification Authority Web Enrollment          ADCS-Web-Enrollment
    [ ] Online Responder                                ADCS-Online-Cert
    [ ] Network Device Enrollment Service               ADCS-Device-Enrollment
    [ ] Certificate Enrollment Web Service              ADCS-Enroll-Web-Svc
    [ ] Certificate Enrollment Policy Web Service       ADCS-Enroll-Web-Pol
[X] Active Directory Domain Services                    AD-Domain-Services
    [X] Active Directory Domain Controller              ADDS-Domain-Controller
    [ ] Identity Management for UNIX                    ADDS-Identity-Mgmt
        [ ] Server for Network Information Services     ADDS-NIS
        [ ] Password Synchronization                    ADDS-Password-Sync
        [X] Administration Tools                        ADDS-IDMU-Tools

The above is just the start of the list that will be returned. The list is quit large so if you know what you want or have an idea you can wildcard it.

 Example: To get the Powershell ISE installed...

C:\>Get-WindowsFeature *ISE*

Display Name                                            Name
------------                                            ----
[ ] Windows PowerShell Integrated Scripting Environm... PowerShell-ISE


Now the cool part is that it not only lists all features available but also what is installed. So one option would be to install the features you require through the GUI and then run this command. It will show you the features that you installed and the  feature name you will have to specify in your Powershell command.

For instance I wanted to install the AD DS tools for Powershell etc..

Here is a section of my output, for RSAT features.



 So to duplicate this in perhaps a build script I would need to run the below command.

Note: Depending on your security restictions you may need to "Run As Administrator".

C:\> Import-Module ServerManager
C:\> Add-WindowsFeature  RSAT-AD-Tools





**After this you will need to restart the computer to see the updated feature list.**

Hope this helps.

All information is provided on an AS-IS basis, with no warranties and confers no rights.