Monday 9 December 2019

SendGrid - Send emails via a Runbook

Case
I want to send email notifications in my Azure Automation runbook when pausing or downscaling fails, but I don't want to create an extra Office365 account for just sending email notifications. How do you send emails in a PowerShell runbook?
Sending free emails in Azure













Solution
Microsoft added the third party service SendGrid to the marketplace under 'Software as a Service (SaaS)' which allows you to send a massive number of 25000 emails a month for free. Hopefully more than enough for a couple of failure notifications.

In this blog we will show you how to create a SendGrid account in Azure and then show you how to send email notifications via a PowerShell runbook. This could be useful to send notifications when pausing or downscaling an Azure resource in your runbook fails. In an other blog post we already showed you how to use SendGrid in Azure LogicApps in combination with Azure Data Factory to send notifications when the ETL process fails.

Part 1: Create SendGrid Account
The first part of this solution is the same as the previous LogicApps post. You can skip it if you already have a SendGrid account.

1) Create new resource
The first step is to create a SendGrid resource which you can find in the Azure marketplace. SendGrid will send the actual emails.
  1. Go to the Azure Portal and create a new resource
  2. Search in de Marketplace for SendGrid or find it under the topic Software as a Service (SaaS)
  3. Select SendGrid and then click on the Create button
  4. Give your new resource a useful name and a secure password
  5. Select the right subscription and Resource Group
  6. Choose the pricing tier you need (F1 is free) and optionally enter a Promotion Code if you expect to send millions of emails a month
  7. Fill out the contact details which will be send to SendGrid (Twilio) for support reasons
  8. Review (and accept) the legal terms
  9. Click on the create button
Create Azure SendGrid account















After these first steps a SendGrid resource will be created in Azure, but it will also create a SendGrid account at sendgrid.com. You will also receive an email to activate your account on sendgrid.com. Note that the pricing details can be changed in Azure, but all the other (non-azure) settings can only be edited on sendgrid.com.

2) Create API key
To send emails via SendGrid we first need the API key. This key can only be generated on the sendgrid.com website.
  1. Go to the Azure SendGrid resource and click on the Manage button on the overview page. This will redirect you to the sendgrid.com website
  2. Go to Settings in the left menu and collapse the sub menu items
  3. Go to the API Keys and click on the Create API Key button.
  4. Then enter a name for your API key, choose which permissions you need and click on the Create and View button.
  5. Copy and save this API key in a password manager. This is the only option you get to retrieve this API key
Retrieve API key















The first part is done. You now have successfully obtained the SendGrid API key which we will use in the second part of this blog.


Part 2: Use SendGrid in Automation Runbooks
Now the coding part of the solution where we will use PowerShell code to send an email.

1) Create Automation Account
First we need to create an Automation Account. If you already have one with the Run As Account enabled then you can skip this step.
  • Go to the Azure portal and create a new resource
  • Search for automation
  • Select Automation Account
  • Choose an useful name for the Automation Account
  • Select your Subscription, Resource Group and the Region
  • Next decide whether you need the Azure Run As account. The first code example does not require it, but the second example (where the SendGrid API key is stored in an Azure Key Vault) does need it. So decide and hit the create button
Create Azure Automation Account















2) Create Runbook
Next we need to create a runbook to host the PowerShell code.
  • Go to the newly created Azure Automation Account
  • Click on Runbooks in the left menu
  • Click on the + Create a runbook button to create a new Runbook
  • Enter a descriptive name for the Runbook
  • Select PowerShell as Runbook type
  • Optionally add a description and click on the Create button

Create Runbook















3) Edit Runbook code
Next edit the new Runbook if it wasn't already opened by the previous step. Copy the code below and paste it in the editor. Then study the code and its comments to understand the code and to think about how to integrate it in your own code.
# PowerShell code

# Hardcode the API key of sendgrid. We need it in the header of the API call
$SENDGRID_API_KEY = "SG.O_g2Bl634FAKEzVOskYInv.8yu_ID2cdxQgoFAKEyG0ByK5xjvPu30DKgOET_n1D7U"

# Create the headers for the API call
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer " + $SENDGRID_API_KEY)
$headers.Add("Content-Type", "application/json")

# Parameters for sending the email
$fromEmailAddress = "noreply@bitools.com"
$destEmailAddress = "joost@bitools.com"
$subject = "Testemail send from runbook via SendGrid"
$content = "Testing 123. This is a test email send via SendGrid"

# Create a JSON message with the parameters from above
$body = @{
personalizations = @(
    @{
        to = @(
                @{
                    email = $destEmailAddress
                }
        )
    }
)
from = @{
    email = $fromEmailAddress
}
subject = $subject
content = @(
    @{
        type = "text/plain"
        value = $content
    }
)
}

# Convert the string into a real JSON-formatted string
# Depth specifies how many levels of contained objects
# are included in the JSON representation. The default
# value is 2
$bodyJson = $body | ConvertTo-Json -Depth 4

# Call the SendGrid RESTful web service and pass the
# headers and json message. More details about the 
# webservice and the format of the JSON message go to
# https://sendgrid.com/docs/api-reference/
$response = Invoke-RestMethod -Uri https://api.sendgrid.com/v3/mail/send -Method Post -Headers $headers -Body $bodyJson

The JSON message that will be send to the webservice looks like this:
{
   "personalizations":[
      {
         "to":[
            {
               "email":"joost@bitools.com"
            }
         ]
      }
   ],
   "from":{
      "email":"no_reply@bitools.com"
   },
   "content":[
      {
         "value":"Testing 123. This is a test email send via SendGrid",
         "type":"text/plain"
      }
   ],
   "subject":"Testemail send from runbook via SendGrid"
}
In the example above the API key is hardcode which is avoidable with some extra steps. In a previous blog we showed you how to store a secret in Azure Key Vault and retrieve it with PowerShell. The code example below shows you how to do that, but please read the Key Vault - Runbook post for more details. The first part is login to Azure and the second change is filling the variable SENDGRID_API_KEY.
# PowerShell code
########################################################
# Log in to Azure with AZ (standard code)
########################################################
Write-Verbose -Message 'Connecting to Azure'
 
# Name of the Azure Run As connection
$ConnectionName = 'AzureRunAsConnection'
try
{
    # Get the connection properties
    $ServicePrincipalConnection = Get-AutomationConnection -Name $ConnectionName       
  
    'Log in to Azure...'
    $null = Connect-AzAccount `
        -ServicePrincipal `
        -TenantId $ServicePrincipalConnection.TenantId `
        -ApplicationId $ServicePrincipalConnection.ApplicationId `
        -CertificateThumbprint $ServicePrincipalConnection.CertificateThumbprint 
}
catch 
{
    if (!$ServicePrincipalConnection)
    {
        # You forgot to turn on 'Create Azure Run As account' 
        $ErrorMessage = "Connection $ConnectionName not found."
        throw $ErrorMessage
    }
    else
    {
        # Something else went wrong
        Write-Error -Message $_.Exception.Message
        throw $_.Exception
    }
}
########################################################

# Retrieve the API key of sendgrid from the Key Vault. We need it in the header of the API call
$SENDGRID_API_KEY = (Get-AzKeyVaultSecret -VaultName "MyVault" -Name "SendGrid").SecretValueText

# Create the headers for the API call
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer " + $SENDGRID_API_KEY)
$headers.Add("Content-Type", "application/json")

# Parameters for sending the email
$fromEmailAddress = "noreply@bitools.com"
$destEmailAddress = "joost@bitools.com"
$subject = "Testemail send from runbook via SendGrid"
$content = "Testing 123. This is a test email send via SendGrid"

# Create a JSON message with the parameters from above
$body = @{
personalizations = @(
    @{
        to = @(
                @{
                    email = $destEmailAddress
                }
        )
    }
)
from = @{
    email = $fromEmailAddress
}
subject = $subject
content = @(
    @{
        type = "text/plain"
        value = $content
    }
)
}

# Convert the string into a real JSON-formatted string
# Depth specifies how many levels of contained objects
# are included in the JSON representation. The default
# value is 2
$bodyJson = $body | ConvertTo-Json -Depth 4

# Call the SendGrid RESTful web service and pass the
# headers and json message. More details about the 
# webservice and the format of the JSON message go to
# https://sendgrid.com/docs/api-reference/
$response = Invoke-RestMethod -Uri https://api.sendgrid.com/v3/mail/send -Method Post -Headers $headers -Body $bodyJson

4) Testing
Testing the functionality of your code can be done in the runbook editor. Click on the Test pane button above your script. After that just hit the Start button to execute the script. The example doesn't have code that writes to the screen. You can add that yourself or just wait for the email the arrive in your mailbox. Below an animation of testing an other script.
Testing PowerShell code in a Runbook














Summary
In this post we showed you how to use SendGrid in an Azure Automation runbook with PowerShell code. To integrate it in your own code you probably want to create a function for it. It is advisable to move the SendGrid API key to an Azure Key Vault to keep your code clean of any passwords or keys.

If you have any suggestions to use SendGrid in other Azure services then please leave a comment below.

7 comments:

  1. hi,
    it is possible to send the email to multiple recipients?
    Simone

    ReplyDelete
    Replies
    1. Yes it is possible by changing the JSON message (email string probably becomes an array of email strings). When I have some time left I will have a look at it an add some sample code. Meanwhile google is your friend

      Delete
  2. Hi,
    has anyone been able yet to change this into an array so multiple recipients can receive emails

    ReplyDelete
  3. Hi,

    You can use a array and store your emails addresses inside that. After that you can use a foreach loop and iterate over it.

    $toEmailAddresses = "abc@gmail.com", "def@gmail.com", "avf@gmail.com"
    foreach($toEmailAddress in $toEmailAddresses)
    {

    # Create a JSON message with the parameters from above
    $body = @{
    personalizations = @(
    @{
    to = @(
    @{
    email = $toEmailAddress
    }
    )
    }
    )
    from = @{
    email = $fromEmailAddress
    }
    subject = $subject
    content = @(
    @{
    type = "text/plain"
    value = $content
    }
    )
    }

    $bodyJson = $body | ConvertTo-Json -Depth 4

    $response = Invoke-RestMethod -Uri https://api.sendgrid.com/v3/mail/send -Method Post -Headers $headers -Body $bodyJson

    ReplyDelete
  4. Hi ,

    I am getting below error in Azure Runbook

    System.Management.Automation.CommandNotFoundException: The term 'Send-SendGridEmail' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
    at System.Management.Automation.CommandDiscovery.LookupCommandInfo(String commandName, CommandTypes commandTypes, SearchResolutionOptions searchResolutionOptions, CommandOrigin commandOrigin, ExecutionContext context)
    at System.Management.Automation.CommandDiscovery.LookupCommandProcessor(String commandName, CommandOrigin commandOrigin, Nullable`1 useLocalScope)
    at System.Management.Automation.ExecutionContext.CreateCommand(String command, Boolean dotSource)
    at System.Management.Automation.PipelineOps.AddCommand(PipelineProcessor pipe, CommandParameterInternal[] commandElements, CommandBaseAst commandBaseAst, CommandRedirection[] redirections, ExecutionContext context)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

    ReplyDelete
  5. hi
    I tried using the above code as it is . But i do not get any email .I use my personal send grid api key .With that key i am able to send email from a python code snippet .

    Any help on this will be much appreciated.

    Regards

    ReplyDelete

All comments will be verified first to avoid URL spammers. यूआरएल स्पैमर से बचने के लिए सभी टिप्पणियों को पहले सत्यापित किया जाएगा।