SMS / MMS / Voice Notifications with PowerShell, System Center Operations Manager and Twilio
Send inexpensive SMS text or MMS messages and voice phone calls to notification subscribers in Operations Manager 2012 using PowerShell and Twilio
Solution Guide
What It Does
This script allows System Center Operations Manager 2012 notifications to be sent via SMS and MMS messages or voice calls by integrating with the internet-based Twilio messaging service (no modem or mobile service required). It can send the dynamic content of an alert either as text in an SMS or MMS message, or machine-read as speech in a voice call. The script integrates as a Notification Channel in Operations Manager, making it easy to use alongside email and instant messaging notifications.
Note: MMS usage is not shown in the video tutorials, but is included.
Usage Scenario
Most Operations Manager deployments use email for notifications of alerts. Sometimes additional methods of contact may be helpful, e.g. having critical alerts trigger a phone call to operations staff after-hours to maximize the chance of notification being delivered (who doesn’t like being woken up at 3 AM?). This script can provide a supplement to email notifications by providing automated SMS text or MMS messages and voice phone calls as alternative communication channels.
This method provides arguably the least expensive means to implement SMS and voice integration. It requires only an Internet connection and a Twilio account, which can be operated in trial mode for free, or for very low cost with a full-featured account (less than $0.01 per message). If you don’t require the robust features of third party notification add-ons like those from Derdack, this is a simple and cheap option.
The script respects the schedules configured for subscribers, so you can limit messages from being sent outside allowed notification windows.
How it Works
The script is invoked as a notification command in Operations Manager, as triggered by a subscription. It is added as a Channel alongside the built-in channels like Email and Instant Message. You can then add it to any Notification Subscription as a channel for delivery. When the subscription is triggered by an alert, the script will be executed and the specified message will be sent to each of the subscribers who has an SMS address (phone number) configured and whose notification schedules allow delivery at the time of the alert.
Twilio Integration
Twilio provides the message delivery. The script uses the Twilio API to connect to the service via HTTP and request delivery of the message to a given recipient. Therefore, a Twilio account must be available and configured for the script to use. It’s worth noting that this means an Internet connection is required for message delivery. Therefore, rest assured that this script will never notify you about your Internet connection being down.
Delivery Methods
Messages can be delivered by SMS text message or voice phone call. The method is determined by the MessageType parameter used when invoking the script (“SMS”, "MMS", or “Voice”). SMS messages are truncated to fit within the SMS 160 character limit, and MMS messages to fit within the MMS 1600 character limit. For voice calls, the message text is read aloud by a robot voice.
Configuration
There are two aspects of configuring the script. First, an XML configuration file is used to provide Twilio account details. Because the account credentials can be used to manipulate the account (whether accidentally or maliciously), ensure the configuration file is protected by reasonable means to be accessible only to the Operations Manager service account and trusted administrators.
Second, the script command-line parameters are used to specify the runtime options. These are documented in the script comments.
MMS vs SMS Message Types
You can choose between SMS and MMS when sending text messages. There are advantages and disadvantages of each:
SMS (text messages)
- Less expensive (see Twilio rates)
- Delivery available in most countries
- Limited to 160 characters per message
MMS ("picture messages")
- More expensive
- Delivery limited by Twilio to certain countries
- Allows 1600 characters, to provide more text from longer alert descriptions
- Allows sending images (optional)
To use MMS, just use "-MessageType MMS" in your command. To include an image, you can provide a URL to any publicly-accessible image on a website. Example command line parameters:
Implementing In Your Environment
This section describes how to get the script working. A step-by-step installation video is also included.
Prerequisites
We’ll assume the following is in place before getting started:
-
System Center Operations Manager 2012 or above installed (obviously, use a test system for testing)
-
PowerShell 3.0 or above installed on Operations Manager management server(s)
-
Solution files downloaded from this page on Automys.com
-
You are mentally prepared for awesomeness
Twilio Setup
First you’ll want to set up a Twilio account. We won’t go into all of the details of this awesome service here, as there is plenty of information on the web about that. For now, getting a free trial account created is sufficient.
-
Go to Twilio.com and follow the signup process. You’ll choose a phone number here.
-
When signup is done, you should find yourself on the getting started page. Here, locate your Twilio number and the API credentials for the account.
-
Trial accounts can only contact phone numbers that are pre-verified in your account. To add numbers you want to call or text while testing, add them under Numbers > Verified Caller IDs.
That’s it. Leave the page up to grab the account info in the next step.
Script Setup
Next, you’ll get the script set up on the Operations Manager management server. This is where it lives, optionally alongside the configuration, trace log, and API library files.
-
Unzip the downloaded solution files into a directory of your choice that is accessible to the Operations Manager service account. For example: C:\OpsMgr_Custom_Scripts\Notifications
-
Open the XML configuration file with notepad. Fill in the AccountSID, AuthToken, and SenderPhoneNumber elements from your Twilio account page. When you finish, it should look something like the below. For the APIFilesPath, leave as “.” unless moving the API files to another folder (in which case, change it to that path). (See example below)
-
Ensure the API library files are present: Twilio.Api.dll and RestSharp.dll.
-
Optional: if wanting to obtain the above files independently for security or to use the latest version, perform the following steps to download. A zip utility like 7-zip must be installed.
-
Open https://www.nuget.org/api/v2/package/Twilio in a browser. This should trigger the download of a “nupkg” file, which is a Nuget package for the .Net Twilio API.
-
Open the file with your zip utility and locate Twilio.Api.dll within lib\3.5. Copy this to the script folder configured above.
-
Open https://www.nuget.org/api/v2/package/RestSharp in a browser. This should trigger the download of a “nupkg” file, which is a Nuget package for the .Net REST API.
-
Open the file with your zip utility and locate RestSharp.dll within lib\net4. Copy this to the script folder configured above.
-
-
Ensure PowerShell script execution is allowed on the server
-
Open a PowerShell prompt as administrator
-
Check the current execution policy. Verify result is RemoteSigned, Unrestricted, or Bypass.
PS >Get-ExecutionPolicy
-
If current policy is none of the above, update the policy. Confirm any prompts.
PS >Set-ExecutionPolicy RemoteSigned
-
Example configuration file:
<?xml version="1.0" encoding="utf-8"?> <!-- This information can be used to modify or use your Twilio account. Ensure it is appropriately protected --> <Settings> <Twilio> <AccountSID>AC0d1dfd9fdb4d14f3f0f8e7eb36a8</AccountSID> <AuthToken>bcf68188673581adf3569bd4c52f</AuthToken> <SenderPhoneNumber>+12244042990</SenderPhoneNumber> <APIFilesPath>.</APIFilesPath> </Twilio> </Settings>
The script is now ready to go. Next, we configure Operations Manager to use it.
Operations Manager Configuration
To integrate the script into Operations Manager, we’ll create a new Channel and then use the channel in a subscription.
-
Open the Operations Console.
-
Go to Notifications > Channels in the Administration workspace.
-
From the tasks pane, select New > Command.
-
Give the new notification channel a name and description, e.g. “Twilio Messaging” / “Sends SMS or voice calls via Twilio service”. Next.
-
The Settings area is where the integration is configured. Configure as follows (see video and script comments for more explanation), and Finish.
Setting
Example Value
Full path of the command file
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Command line parameters
-NoProfile -File Send-TwilioMessage.ps1 -MessageType SMS -MessageText "Ops Alert: $Data[Default='Not Present']/Context/DataItem/AlertName$" -SubscriptionID "$MPElement$" -EnableTraceLogging
Startup folder
C:\OpsMgr_Custom_Scripts\Notifications
-
Create or open one or more subscribers for testing from Notifications > Subscribers.
-
In the Addresses area, add a new address of type Text Message (SMS). Include a valid phone number as the delivery address (E.164 format recommended). Schedules can be left at default for testing.
-
Create a placeholder subscriber to use for subscription triggering. This can be any non-human account (e.g. an Operations Manager service account), and is used only to cause the subscription to trigger our script command channel. This subscriber will not have an address or receive notifications.
-
Create new subscriber, selecting the domain account to use
-
For schedule, select Always send notifications.
-
In the addresses area, add a new address. Name the address “Twilio Command Placeholder”.
-
For the address channel, select the Command channel type and the new command channel created above. No address is configured here.
-
For address schedule, select Always send notifications.
-
-
Under Notifications > Subscriptions, create a New test subscription from the task pane.
-
Provide name and description, .e.g. “SMS Alerts”.
-
In the Criteria area, specify any criteria that will match the alert you will use for testing purposes.
-
In Subscribers, add the SMS-enabled subscribers you configured above as well as the placeholder account.
-
In Channels, add the new channel created above.
-
Finish the wizard.
At this point, Operations Manager is configured via our new subscription to watch for certain alerts that will cause it to trigger SMS notification via our fancy script command channel.
Using Multiple Message Types
In the Channel setup above, we specified a MessageType in the script parameters. This determines whether the message is sent via SMS, MMS or voice call. What if we want to have multiple delivery types, perhaps in different subscriptions or circumstances? For example, perhaps you want a subscription for critical off-hours alerts that uses voice call notifications, and a different subscription for lesser severity alerts that uses SMS and Email only.
To accomplish this, set up two notification channels each with a different MessageType parameter, for example one with “–MessageType SMS” and one with “–MessageType Voice”, leaving the other settings the same. Then, you can choose which to use in a given subscription.
Kicking the Tires
The moment of glory is near. Before testing from Operations Manager, it is easiest to test from the command line and ensure the script runs successfully. Once that is confirmed, we’ll move on to get an Operations Manager alert to trigger via subscription.
Manual Test
To manually test, open a PowerShell prompt and change to your script directory:
PS C:\Users\Noah> cd C:\OpsMgr_Custom_Scripts\Notifications
Now, call the script using a test message and the name of the test subscription. It’s also helpful to include the EnableTraceLogging switch parameter to see a detailed log of everything that happens:
.\Send-TwilioMessage.ps1 -MessageText "This is ground control to Major Tom" -MessageType SMS -SubscriptionID "SMS Alerts" –EnableTraceLogging
With any luck, you’ll receive a text message within a few seconds. Open the trace.log file in the script directory to see details, including any error messages that may have occurred.
If SMS isn’t exciting enough, also test a voice call:
.\Send-TwilioMessage.ps1 -MessageText "I’m stepping through the door and floating in a most peculiar way" -MessageType Voice -SubscriptionID "SMS Alerts" –EnableTraceLogging
And a robotic reading should greet your ears in a moment.
Note that for trial accounts, messages will include an addition about trial status. This is removed when upgrading to a paid account.
Subscription Test
To test the full integration, all that’s needed now is to trigger an alert that is configured in the test subscription. I like to use a custom task and rule that creates a synthetic/fake event in the event log in order to trigger an alert just for testing. It’s also helpful to add email as a channel to the test subscription such that an email is sent at the same time you expect a notification via SMS or voice. This way, you have an easy way to know the subscription triggered if you don’t receive a corresponding SMS or call.
Schedule Testing
You may also want to test any schedules that you implement on the subscriptions to limit when SMS / voice notifications are used. The script respects the schedules configured on both subscribers and their individual addresses, so you could limit SMS to be used only after business hours, for example, or calls to happen only on weekends and lunch hours, or any other time people would rather not be bothered.
Readying for Production
After testing demonstrates everything is working, remember a couple considerations before going live.
First, consider removing the EnableTraceLogging switch from the command parameters for the Command Channel. This performs detailed logging that may grow into a large file in production. If leaving it on, make sure Operations Manager is monitoring your disk space.
Second, it is probably not advisable to try using a Twilio trial account past the testing phase. It’s cheap and they provide an excellent service, so pony up.
As always, please reach out to me if you found this helpful, have questions, or are interested in working together on something more advanced. Enjoy!
Script
<# .SYNOPSIS Sends a message via SMS or voice call using the Twilio messaging service to recipients defined in the specified System Center Operations Manager subscription. .DESCRIPTION This script is intended to be invoked as a notification command in System Center Operations Manager 2012/R2. It accepts a message in the form of a string and sends to a list of phone numbers retrieved from the recipients defined in the specified subscription. The message is truncated to fit with the 160 character SMS limit and delivered via the Twilio messaging service (see www.twilio.com). A valid Twilio account must be provided to the script via an associated configuration file. Additionally, the .Net library for the Twilio API must be available in a configured path on the system. The recipients must be Subscribers defined in Operations Manager with a "Text Message (SMS)" address defined, which is a phone number. The number can be in one of several formats, though Twilio prefers E.164, e.g.(for US) +15551234567. .PARAMETER MessageText The text of the message to send as a string. Will be truncated to fit within the 160 character SMS limit. For voice calls, Twilio will perform text-to-speech conversion. .PARAMETER MessageType Choice of SMS, MMS or Voice. SMS will be delivered as a normal text message with max length of 160 characters. MMS will be delivered as MMS message with max length 1600 characters. Limited to US/Canada only by Twilio as of April 2015. Voice will be delivered as a voice phone call with machine reading of the MessageText. .PARAMETER SubscriptionID The ID property of the Operations Manager subscription that defines recipients for this message. Preferred format is "{GUID}" and is provided in notifcation channel command parameters using the "$MPElement$" substituion variable. Alternatively, script will attempt to use as DisplayName to find subscription by name rather than ID. .PARAMETER ConfigurationFilePath The directory containing the XML configuration file which defines the Twilio account SID, auth token, sending phone number, and API files path. .PARAMETER EnableTraceLogging Switch to enable trace logging to a file in the same directory as the script. Unless included, no trace data will be logged. .PARAMETER MMSImageURL Optional URL of a publicly-accessible image to include in the MMS message. This is read by Twilio and inserted as image content in the message. .EXAMPLE To manually test with trace logging from a PowerShell prompt using a subscription ID obtained from Operations Manager: .\Send-TwilioMessage.ps1 -MessageText "London Bridge is falling down!" -MessageType SMS -SubscriptionID "{5B2E1566-39E8-DB71-4A19-2C55FEF4829A}" -EnableTraceLogging .EXAMPLE To manually test a voice call from a PowerShell prompt using a subscription ID obtained from Operations Manager: .\Send-TwilioMessage.ps1 -MessageText "London Bridge is falling down!" -MessageType Voice -SubscriptionID "{5B2E1566-39E8-DB71-4A19-2C55FEF4829A}" .EXAMPLE Configured as command parameters to powershell.exe in Operations Manager notification channel command, sending the alert name as an SMS message: -File Send-TwilioMessage.ps1 -MessageType SMS -MessageText "OpsMgr Alert: $Data[Default='Not Present']/Context/DataItem/AlertName$" -SubscriptionID "$MPElement$" .INPUTS None. .OUTPUTS No objects returned. .NOTES For more details and implementation guidance, see the associated documentation at https://automys.com #> [CmdletBinding()] Param( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$MessageText, [ValidateSet("SMS","MMS","Voice")] [string]$MessageType = "SMS", [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$SubscriptionID, [string]$ConfigurationFilePath, [switch]$EnableTraceLogging, [string[]]$MMSImageURL ) # Define function to add entry to trace log located in same folder as script function AppendLog ([string]$Message) { if($EnableTraceLogging -eq $true) { Add-Content -Path $logPath -Value ((Get-Date).ToString() + "`t" + $Message) } } # Define function to check the master and address schedules for an individual subscriber # Returns true if current time conforms to schedules, false if not function CheckSubscriberSchedules ($Subscriber, $Address) { $scheduleValidated = $true # Check against subscriber master schedule(s). If any violated, overall check not satisifed. foreach($scheduleEntry in $Subscriber.ScheduleEntries) { if((CheckSchedule -Schedule $scheduleEntry) -eq $false) { $scheduleValidated = $false } } # Check against subscriber address (phone number) schedule(s). If any violated, overall check not satisifed. foreach($scheduleEntry in $Address.ScheduleEntries) { if((CheckSchedule -Schedule $scheduleEntry) -eq $false) { $scheduleValidated = $false } } return $scheduleValidated } # Define function to convert a NotificationRecipientScheduleEntry time range object to a standard DateTime format in the specified time zone function ConvertTimeRange ($EntryTime, $TimeZone) { $convertedTime = Get-Date -Hour $EntryTime.Hour -Minute $EntryTime.Minute -Second 0 return [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId($convertedTime, $scheduleTimeZone) } # Define function to check a single schedule # Returns true if current time conforms to schedule, false if not function CheckSchedule ($Schedule) { if($Schedule -eq $null) { return $true } # Begin with no violations, setting if found $scheduleViolated = $false # Get current time in target time zone $scheduleTimeZone = $Schedule.TimeZone.Substring($Schedule.TimeZone.IndexOf("|") + 1) $now = [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([DateTime]::UtcNow, $scheduleTimeZone) # Date range check # If date range defined and current time is outside the range, record violation. Otherwise, check passes. if($Schedule.ScheduledStartDate -ne $null -and $Schedule.ScheduledEndDate -ne $null) { if($now -lt $Schedule.ScheduledStartDate -or $now -gt $Schedule.ScheduledEndDate) { $scheduleViolated = $true } } # Daily time range check # If current time is outside the daily time range, record violation. Otherwise, check passes. if($now -lt (ConvertTimeRange -EntryTime $Schedule.DailyStartTime -TimeZone $scheduleTimeZone) -or $now -gt (ConvertTimeRange -EntryTime $Schedule.DailyEndTime -TimeZone $scheduleTimeZone)) { $scheduleViolated = $true } # Day of week test $allowedDays = @() $scheduleDaysString = $Schedule.ScheduledDays.ToString() $scheduleDaysString = $scheduleDaysString -replace "Weekdays","Monday,Tuesday,Wednesday,Thursday,Friday" $scheduleDaysString = $scheduleDaysString -replace "WeekendDays","Saturday,Sunday" switch ($scheduleDaysString) { "None" { # No days allowed } "All" { $allowedDays += "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" } default { # One or more days by name, comma separated $allowedDays += $scheduleDaysString -replace " ","" -split "," } } # If today is not in the list of allowed days, record violation if(($allowedDays -contains $now.DayOfWeek) -eq $false) { $scheduleViolated = $true } # Determine overall result # If now is outside the schedule and we wanted to be inside, return false to indicate schedule not satisifed if ($Schedule.ScheduleEntryType -eq "Inclusion" -and $scheduleViolated -eq $true) { return $false } # If now is within the schedule but we wanted to exclude these times, return false to indicate schedule not satisifed elseif ($Schedule.ScheduleEntryType -eq "Exclusion" -and $scheduleViolated -eq $false) { return $false } # Otherwise, the schedule was satisifed else { return $true } } function TruncateMessage ($MessageText, $CharacterLimit) { $ellipses = "..." $contentLimit = $CharacterLimit - $ellipses.Length if($MessageText.Length -gt $contentLimit) { $MessageText = $MessageText.Substring(0, $contentLimit) + $ellipses } return $MessageText } # Test access to log file, create new name if denied (likely created by another user or process) $logPath = $PSScriptRoot + "\trace.log" try { [IO.File]::OpenWrite($logPath).Close() } catch { $logSuffix = Get-Date -Format "yyyyMMddhhMMss" $logPath = "$PSScriptRoot\trace-$logSuffix.log" } if($ConfigurationFilePath.Length -eq 0) { $ConfigurationFilePath = $PSScriptRoot + "\Send-TwilioMessage_config.xml" } AppendLog -Message "Script started" AppendLog -Message "Running as user [$([Environment]::UserDomainName)\$([Environment]::UserName)]" AppendLog -Message "MessageText=[$MessageText]; MessageType=[$MessageType]; SubscriptionID=[$SubscriptionID]; ConfigurationFilePath=[$ConfigurationFilePath]" try { AppendLog -Message "Reading configuration file" # Check for the expected configuration file if((Test-Path $ConfigurationFilePath) -eq $false) { throw "Configuration file not found at expected path: $ConfigurationFilePath" } # Read and validate configuration parameters from file [xml]$configFile = Get-Content $ConfigurationFilePath if($configFile -eq $null -or $configFile.Settings -eq $null -or $configFile.Settings.Twilio -eq $null) { throw "Error reading the configuration file $ConfigurationFilePath. Verify the format is correct." } if($configFile.Settings.Twilio.AccountSID.Length -eq 0) { throw "No value defined for AccountSID in configuration file" } if($configFile.Settings.Twilio.AuthToken.Length -eq 0) { throw "No value defined for AuthToken in configuration file" } if($configFile.Settings.Twilio.SenderPhoneNumber.Length -eq 0) { throw "No value defined for SenderPhoneNumber in configuration file" } if($configFile.Settings.Twilio.APIFilesPath.Length -eq 0) { $configFile.Settings.Twilio.APIFilesPath = "." } # Check for Twilio API library files AppendLog -Message "Loading Twilio API library" $libraryFilesPath = $configFile.Settings.Twilio.APIFilesPath.TrimEnd('\') if($libraryFilesPath -eq ".") { # If "." path configured, expect files in the same folder as script $libraryFilesPath = $PSScriptRoot } $libraryFileList = "Twilio.Api.dll","RestSharp.dll" foreach($fileName in $libraryFileList) { $filePath = $libraryFilesPath + "\" + $fileName if((Test-Path $filePath) -eq $false) { throw "Required API file $fileName not found at expected path $libraryFilesPath" } } # Load Twilio .NET API library Add-Type -Path ($libraryFilesPath + "\" + "Twilio.Api.dll") # Create Twilio client object $twilioClient = New-Object Twilio.TwilioRestClient($configFile.Settings.Twilio.AccountSID, $configFile.Settings.Twilio.AuthToken) # Verify access to account AppendLog -Message "Validating Twilio account" $accountTest = $twilioClient.GetAccount() if($accountTest -eq $null -or $accountTest.Sid.Length -eq 0) { $errorMessage = "Failed to access Twilio account. Validate the account SID and auth token in the configuration file match the API Credentials shown at https://www.twilio.com/user/account." if($accountTest.RestException -ne $null -and $accountTest.RestException.Message.Length -gt 0) { $errorMessage += " Details: " + $accountTest.RestException.Message } throw $errorMessage } # Load Operations Manager PowerShell module AppendLog -Message "Loading Operations Manager PowerShell module" Import-Module OperationsManager if((Get-Module OperationsManager) -eq $null) { # If not loaded by name, try the path to module from Registry $modulePath = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\System Center Operations Manager\12\Setup\Powershell\V2" | select -ExpandProperty InstallDirectory $modulePath += "\OperationsManager\OperationsManager.psd1" Import-Module $modulePath } if((Get-Module OperationsManager) -eq $null) { # Not found by either method. Throw error and exit. throw "Failed to load PowerShell module for System Center Operations Manager" } # Get the notifcation subscription specified as parameter. Try both name and GUID. AppendLog -Message "Retrieving subscription and recipient details" $subscription = Get-SCOMNotificationSubscription | where {$_.Id -eq $SubscriptionID -or $_.DisplayName -eq $SubscriptionID} # Validate subscription if($subscription -eq $null) { throw "No notification subscription found with ID [$SubscriptionID]" } AppendLog -Message "Found subscription [$($subscription.DisplayName)]" # Get list of recipients to subscription $recipientList = $subscription.ToRecipients + $subscription.CcRecipients + $subscription.BccRecipients # Validate recipient list if($recipientList -eq $null -or $recipientList.Count -eq 0) { throw "No recipients found for subscription with ID [$SubscriptionID]" } AppendLog -Message "Found recipients: [$(($recipientList | select -ExpandProperty Name) -join ",")]" # Get phone number list for recipients $phoneList = @() foreach($recipient in $recipientList) { $smsAddress = $recipient.Devices | where Protocol -eq SMS if($smsAddress -ne $null -and (CheckSubscriberSchedules -Subscriber $recipient -Address $smsAddress) -eq $false) { AppendLog -Message "Schedules for $($recipient.Name) do not allow SMS/Voice notifications at current time" } $phoneNumber = $smsAddress.Address if($phoneNumber.Length -gt 0) { $phoneList += $phoneNumber } else { AppendLog -Message "No phone number configured for recipient: $($recipient.Name)" } } # Validate phone list if($phoneList.Count -eq 0) { throw "No phone numbers found for recipients of subscription with ID [$SubscriptionID]" } AppendLog -Message "Found phone numbers: [$($phoneList -join ",")]" # Replace any line breaks to be parsed correctly $MessageText = $MessageText -replace "\\n","`n" # Send message via the specified method to each recipient AppendLog -Message "Sending messages" $errorList = @() foreach($recipient in $phoneList) { switch($MessageType) { "SMS" { # Truncate message to SMS limit $SMS_CHARACTER_LIMIT = 160 $MessageText = TruncateMessage -MessageText $MessageText -CharacterLimit $SMS_CHARACTER_LIMIT # Send SMS message using supplied message text $sendResult = $twilioClient.SendSmsMessage($configFile.Settings.Twilio.SenderPhoneNumber, $recipient, $MessageText) } "MMS" { # Truncate message to MMS limit $MMS_CHARACTER_LIMIT = 1600 $MessageText = TruncateMessage -MessageText $MessageText -CharacterLimit $MMS_CHARACTER_LIMIT # Send MMS message using supplied message text including image if specified if($MMSImageURL.Length -gt 0) { $sendResult = $twilioClient.SendMessage($configFile.Settings.Twilio.SenderPhoneNumber, $recipient, $MessageText, $MMSImageURL) } else { $sendResult = $twilioClient.SendMessage($configFile.Settings.Twilio.SenderPhoneNumber, $recipient, $MessageText) } } "Voice" { # Build voice message URL [Reflection.Assembly]::LoadWithPartialName("System.Web") | Out-Null $encodedText = [System.Web.HttpUtility]::UrlEncode($MessageText) $messageURL = "http://twimlets.com/message?Message%5B0%5D=" + $encodedText # Initiate the voice phone call $sendResult = $twilioClient.InitiateOutboundCall($configFile.Settings.Twilio.SenderPhoneNumber, $recipient, $messageURL) } } # Check results if($sendResult -eq $null -or $sendResult.Status.Length -eq 0) { $errorMessage = "Failed to send message." if($sendResult.RestException -ne $null -and $sendResult.RestException.Message.Length -gt 0) { $errorMessage += " Exception details: Status=[" + $sendResult.RestException.Status + "], Message=[" + $sendResult.RestException.Message + "]" } $errorList += $errorMessage } else { AppendLog -Message "Successfully sent message to [$recipient]" } } # Validate results if($errorList.Count -gt 0) { $errorString = $errorList -join ";" throw "Encountered $($errorList.Count) failures while sending messages. Details: [$errorString]" } } catch { AppendLog -Message "ERROR: $($error[0].Exception.Message)" } finally { AppendLog -Message "Script finished" }