Skip to content

Instantly share code, notes, and snippets.

@mortenlerudjordet
Created February 12, 2017 12:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mortenlerudjordet/8261b8fa21dba09d3a03b1f926a1e43d to your computer and use it in GitHub Desktop.
Save mortenlerudjordet/8261b8fa21dba09d3a03b1f926a1e43d to your computer and use it in GitHub Desktop.
Sends a message via SMS using PSWinCom Gateway
<#
.SYNOPSIS
Sends a message via SMS using PSWinCom Gateway to
recipients defined in the specified System Center Operations Manager subscription.
All activity is logged to the eventlog defined in $EventLog with source name $EventSource.
For logging to work New-EventLog -LogName $EventLog -Source $EventSource must be first
executed on all SCOM MGMT servers the script will run on beforehand.
.DESCRIPTION
This script is intended to be invoked as a notification command in System Center
Operations Manager 2012/R2/2016. 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 PSWinCom messaging service (see wiki.pswin.com).
A valid PSWinCom account must be defined in the script or no SMS will be sent.
The recipients must be Subscribers defined in Operations Manager with a "Text Message (SMS)"
address field, which is a phone number. The number must be a 8 character or more. If +(countrycode)
is not defined +47 (Norway) will be automatically added by the script, this can be changed in the script if needed
.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, PSWinCom will perform text-to-speech conversion.
.PARAMETER MessageType
Choice of SMS or Voice (not implemented). SMS will be delivered as a normal text message. Voice will
be delivered as a voice phone call with machine reading of the MessageText.
Only SMS is supported for PSWinCom service.
.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. Script will strip the brackets from the input before using it.
.EXAMPLE
To manually test with trace logging from a PowerShell prompt using a subscription ID obtained from Operations Manager:
.\Send-PSWinCSMS.ps1 -MessageText "Danger! Will Robinson" -MessageType SMS -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:
Full path of the commandfile:
c:\windows\system32\windowspowershell\v1.0\powershell.exe
Command line parameters:
c:\scripts\Send-PSWinCSMS.ps1 -MessageType 'SMS' -MessageText 'AlertName: $Data[Default='Missing Alert Name']/Context/DataItem/AlertName$%0a$Data[Default='Not Present']/Context/DataItem/AlertDescription$' -SubscriptionID '$MPElement$'
Startup:
c:\scripts
Note: use %0a in the commandline parameters where you want a new line inserted into the SMS text content
.INPUTS
MessageType
MessageText
SubscriptionID
.OUTPUTS
No objects returned.
.NOTES
NAME: Send-PSWinCSMS.ps1
AUTHOR: Morten Lerudjordet
VERSION: 1.0
LASTEDIT: 13.01.2017
Original: https://gallery.technet.microsoft.com/scriptcenter/SMS-Voice-Notifications-68602423
#>
[CmdletBinding()]
Param(
[ValidateSet("SMS")]
[string]$MessageType = "SMS",
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$MessageText,
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$SubscriptionID
)
#region internal parameters
$MESSAGE_CHARACTER_LIMIT = 160
$SMSBaseServiceURL = "https://simple-lb1.pswin.com/"
# Must not be above 15 chars have space or +
$SenderID = "Alert"
$UserName = ""
$Password = ""
$EventId = 5555
$EventSource = "SCOMSendSMS"
$EventLog = "Application"
# Logging using MOMscriptAPI
$SCRIPT_NAME = "Send-PSWinCSMS"
$SCRIPT_EVENTid = 5555
# 0 = Info, 1 = Error, 2 = Warning
$Error_EVENT = 1
$Warning_EVENT = 2
$Info_EVENT = 0
#endregion internal parameters
#region Functions
# 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, $TimeZone)
}
# 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($Schedule.DailyStartTime -ne $null -and $Schedule.DailyEndTime -ne $null) {
# Exclusion can only be set for time
If($Schedule.ScheduleEntryType -eq "Exclusion") {
if($now -ge (ConvertTimeRange -EntryTime $Schedule.DailyStartTime -TimeZone $scheduleTimeZone) -or $now -le (ConvertTimeRange -EntryTime $Schedule.DailyEndTime -TimeZone $scheduleTimeZone))
{
$scheduleViolated = $true
}
}
Else {
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" {
# All 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 ($scheduleViolated -eq $true)
{
return $false
}
# Otherwise, the schedule was satisifed
else
{
return $true
}
}
function CreateSMSurl {
[CmdletBinding()]
Param(
[parameter(mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$UserName,
[parameter(mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$Password,
[parameter(mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$PhoneNumber,
[parameter(mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$SenderID,
[parameter(mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$SMSserviceURL
)
# Check for valid numbers
If($PhoneNumber -eq -1) {
# No phone number
Return -1
}
# Check phone number for country code
Else {
# If phone number is less that 8 digits add +47 (Norway)
If((Measure-Object -InputObject $PhoneNumber -Character).Characters -le 8) {
$PhoneNumber = "+47" + $PhoneNumber
}
# Add + sign before phone number if length is only 10 digits
ElseIf((Measure-Object -InputObject $PhoneNumber -Character).Characters -eq 10) {
$PhoneNumber = "+" + $PhoneNumber
}
[String]$url = $SMSserviceURL `
+ "?USER=" + $UserName `
+ "&PW=" + $Password `
+ "&RCV=" + $PhoneNumber `
+ "&SND=" + $SenderID `
+ "&TXT="
Return $url
}
}
#endregion Functions
#region Main
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId `
-EntryType Information -Message "[$SCRIPT_NAME] Starting up" -ErrorAction SilentlyContinue
# Note New-EventLog -LogName $EventLog-Source $EventSource must have been run before script can log to eventlog on computer it is running on
# Create MOM script API object
<#
$api = New-Object -comObject "MOM.ScriptAPI" -ErrorAction Continue -ErrorVariable oErr
# Check for error in creating object
If($oErr)
{
# Write Error to application log and stop script, SCOMSendSMS må ha blitt registrert på server scriptet skal kjøre med New-EventLog -LogName Application -Source SCOMSendSMS
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId -EntryType Error -Message "Could create SCOM script API object" -ErrorAction Stop
$oErr = $Null
# stopping script execution
Write-Error -Message "Could create SCOM script API object, halting" -ErrorAction Stop
}
#>
# Load Operations Manager PowerShell module
Import-Module -Name OperationsManager -ErrorAction SilentlyContinue
if((Get-Module -Name OperationsManager -ErrorAction SilentlyContinue) -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-Object -ExpandProperty InstallDirectory
$modulePath = Join-Path -Path $modulePath -ChildPath "OperationsManager\OperationsManager.psd1"
Import-Module -Name $modulePath -ErrorAction SilentlyContinue -ErrorVariable oErr
# Check for error loading module
if($oErr)
{
# Not found by either method. Throw error and exit.
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId -EntryType Error `
-Message "Could not import OperationsManager module, halting" -ErrorAction SilentlyContinue
$oErr = $Null
# stopping script execution
Write-Error -Message "Could import OperationsManager module, halting" -ErrorAction Stop
}
}
# logging usercontext
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId `
-EntryType Information -Message "Running as user [$([Environment]::UserDomainName)\$([Environment]::UserName)]"
# Logging input parameters
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId `
-EntryType Information -Message "MessageText=[$MessageText]; MessageType=[$MessageType]; SubscriptionID=[$SubscriptionID];"
Try
{
# Remove {} from input
$SubscriptionID = $SubscriptionID.Trim("{","}")
# Get subscription from SCOM
$Subscription = Get-SCOMNotificationSubscription -Id $SubscriptionID -ErrorAction SilentlyContinue -ErrorVariable oErr
# Check for errors or missing subscription
If($oErr -or (-not $Subscription))
{
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId `
-EntryType Error -Message "Could not find a valid subscription for: $SubscriptionID" -ErrorAction SilentlyContinue
$oErr = $Null
# stopping script execution
Write-Error -Message "Could not find a valid subscription for: $SubscriptionID, halting" -ErrorAction Stop
}
Else
{
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId `
-EntryType Information -Message "Found subscription: [$($Subscription.DisplayName)]" -ErrorAction SilentlyContinue
}
# Get list of recipients to subscription
$RecipientList = @($subscription.ToRecipients) + @($Subscription.CcRecipients) + @($Subscription.BccRecipients)
# Validate recipient list
if((-not $RecipientList) -or $RecipientList.Count -eq 0)
{
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId `
-EntryType Error -Message "Could not find any recipients in subscription: $SubscriptionID" -ErrorAction SilentlyContinue
$oErr = $Null
# stopping script execution
Write-Error -Message "Could not find any recipients in subscription: $SubscriptionID, halting" -ErrorAction Stop
}
# Get phone number list for recipients
$PhoneList = @()
foreach($Recipient in $RecipientList)
{
# Get all recipients with SMS phone number defined
[Array]$smsAddresses = $recipient.Devices | Where-Object {$_.Protocol -eq "SMS"}
ForEach($smsAddress in $smsAddresses) {
If($smsAddress -ne $null -and (CheckSubscriberSchedules -Subscriber $Recipient -Address $smsAddress) -eq $false)
{
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId `
-EntryType Information -Message "Schedules for $($recipient.Name) do not allow message notifications at current time" -ErrorAction SilentlyContinue
}
Else {
[String]$PhoneNumber = $smsAddress.Address
# Check that number has more than 7 digits
If($PhoneNumber -and $PhoneNumber.Length -ge 8)
{
# Add number to list of recipients of SMS
$PhoneList += $PhoneNumber
}
Else
{
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId `
-EntryType Warning -Message "No phone number configured for recipient: $($recipient.Name)" -ErrorAction SilentlyContinue
}
}
}
}
# Validate phone list
If($PhoneList.Count -eq 0)
{
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId `
-EntryType Error -Message "No phone numbers found for recipients of subscription with ID [$SubscriptionID]" -ErrorAction SilentlyContinue
}
# Truncate message to fit within SMS limit
If($MessageText.Length -gt ($MESSAGE_CHARACTER_LIMIT - 3))
{
$MessageText = $MessageText.Substring(0, $MESSAGE_CHARACTER_LIMIT - 3) + "..."
}
ForEach($PhoneNumber in $PhoneList)
{
switch($MessageType)
{
"SMS" {
# Send SMS message using supplied message text
$URL = CreateSMSurl -SMSserviceURL $SMSBaseServiceURL -UserName $UserName -Password $Password `
-PhoneNumber $PhoneNumber -SenderID $SenderID -ErrorAction SilentlyContinue -ErrorVariable oErr
If((-not $oErr) -or $URL -ne -1)
{
$Message = $URL + $MessageText
$Response = Invoke-RestMethod -Uri $Message -Method Get -ErrorAction SilentlyContinue -ErrorVariable oErr -Verbose:$False
}
Else
{
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId `
-EntryType Error -Message "Error calling function CreateSMSurl" -ErrorAction SilentlyContinue
$oErr = $Null
}
}
<#
"Voice" {
# Not implemented
}
#>
}
# Check results for errors
If($oErr)
{
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId `
-EntryType Error -Message "Error calling url: [$SMSserviceURL] could not send message to: [$($recipient.Name)] using number: [$PhoneNumber]" -ErrorAction SilentlyContinue
$oErr = $Null
}
Else
{
# Remove whitespaces
$Response = $Response.Trim()
# Keep last part of string (OK)
$Response = ($Response -split "`r`n|`r|`n")[-1]
If($Response -ne "OK")
{
# Check for errors
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId `
-EntryType Error -Message "Message could not be sent to: [$($recipient.Name)] using number: [$PhoneNumber]" -ErrorAction SilentlyContinue
}
Else
{
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId `
-EntryType Information -Message "Successfully sent message to: [$($recipient.Name)] using number: [$PhoneNumber]" -ErrorAction SilentlyContinue
}
}
}
}
catch
{
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId `
-EntryType Error -Message "Terminating error occured: $($error[0].Exception.Message)"
}
finally
{
Write-EventLog -LogName $EventLog -Source $EventSource -EventId $EventId `
-EntryType Information -Message "[$SCRIPT_NAME] Finished run" -ErrorAction SilentlyContinue
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment