How to send SMS with C# .NET and Azure Functions using Twilio Output Binding
Tags: azure, dotnet, csharp, twilio
Published: 2022-01-25 19:49:19

---
title: How to send SMS with C# .NET and Azure Functions using Twilio Output Binding
published: true
date: 2022-01-24 00:00:00 UTC
tags: azure, dotnet, csharp, twilio
canonical_url: https://www.twilio.com/blog/send-sms-with-csharp-dotnet-and-azure-functions-using-the-twilio-output-binding
cover_image: https://swimburger.net/media/zplliflb/how-to-send-sms-with-c-net-azure-functions-using-twilio-output-binding-canonical.png
---

**This blog post was written for [Twilio](https://www.twilio.com/) and originally published at [the Twilio blog](https://www.twilio.com/blog/send-sms-with-csharp-dotnet-and-azure-functions-using-the-twilio-output-binding).**

Azure Functions has its own opinionated way of developing applications based on triggers, input bindings and output bindings. Azure supports two Twilio products using output bindings: [Twilio Programmable Messaging](https://www.twilio.com/messaging) for sending SMS and [Twilio SendGrid](https://sendgrid.com/) for sending emails. In this tutorial, you'll learn how to send text messages with C# .NET using Azure Functions and Twilio binding.

## Prerequisites

You will need these items to follow along:

- OS that supports .NET (Windows/Mac/Linux)
- [.NET 6 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)
- [Azure Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local#install-the-azure-functions-core-tools)
- A code editor or IDE (Recommended: VS Code with C# plugin, Visual Studio, or JetBrains Rider)
- A free Twilio account (If you [register here](https://www.twilio.com/referral/OhKZOM), you'll receive $10 in Twilio credit when you upgrade to a paid account!)

## Get started with Twilio

You will need to set up a couple of things with Twilio before developing the Azure function:

- If you don't already have a Twilio phone number, go and [buy a new phone number from Twilio](https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console). The cost of the phone number will be applied to your free promotional credit.  
Make sure to take note of your new Twilio phone number. You'll need it later on!
- If you are using a trial Twilio account, you can only send text messages to Verified Caller IDs. [Verify your phone number](https://support.twilio.com/hc/en-us/articles/223180048-Adding-a-Verified-Phone-Number-or-Caller-ID-with-Twilio) or the phone number you want to SMS if it isn't on the list of Verified Caller IDs. 
- Lastly, you'll need to find your Twilio Account SID and Auth Token. Navigate to your [Twilio account page](https://console.twilio.com/) and **take note of your Twilio Account SID and Auth Token** located at the bottom left of the page.  
 ![Account Info box holding 3 read-only fields: Account SID field, Auth Token field, and Twilio phone number field.](https://twilio-cms-prod.s3.amazonaws.com/images/Zz829kWqd7Pe5_Da6tSmN7B8K7JrlS2KtOeQH6ERwN7slH.width-500.png)

## Preparing your local Azure Functions environment

You will need to prepare your development machine depending on the type of Azure Functions you will be developing. HTTP trigger based functions don't require any extra steps, but timer trigger based functions and other types of Azure Functions depend on Azure Storage. Luckily, Azure has developed a cross-platform and open-source storage emulator called [Azurite](https://github.com/Azure/Azurite). You'll need to install and run Azurite before you can develop Azure Functions locally.

There are [multiple ways to install Azurite](https://docs.microsoft.com/en-us/azure/storage/common/storage-use-azurite?tabs=npm), but one of the easiest ways is to install it as a global NPM package. This means you need to have [Node.js version 8.0 or later](https://nodejs.org/) installed on your machine.

To install Azurite as a global NPM package, run the following command:

```bash
npm install -g azurite
```

When you run Azurite, it will create several files and folders in the current directory. Create a new directory and navigate to it to avoid accidentally polluting your current directory:

```bash
mkdir Azurite
cd Azurite
```

Run Azurite using the following command:

```bash
azurite
```

Leave Azurite running in your current shell and open a new shell for all future commands. Make sure the new shell is not opened inside the **_Azurite_**  **folder.**

## Create an Azure Function

It's time to start developing your Azure functions. Run the following command in the new shell you opened in the previous step:

```bash
func init AzureFunctionsWithTwilioBindings --dotnet
```

This command will create a new folder _AzureFunctionsWithTwilioBindings_ and generate a .NET project for Azure Functions. 

Navigate to the new folder:

```bash
cd AzureFunctionsWithTwilioBindings
```

This project does not contain any functions yet. Run the following command to generate a timer trigger based Azure Function.

```bash
func new --name SendSmsTimer --template "Timer trigger"
```

This will create a new C# file: _SendSmsTimer.cs_.

Open the _SendSmsTimer.cs_ file and replace "0 \*/5 \* \* \* \*" with "\*/15 \* \* \* \* \*".

The _SendSmsTimer.cs_ file should now look like this:

```csharp
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
 
namespace SampleAzureFunctions
{
    public class SendSmsTimer
    {
        [FunctionName("SendSmsTimer")]
        public void Run([TimerTrigger("\*/15 \* \* \* \* \*")]TimerInfo myTimer, ILogger log)
        {
            log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
        }
    }
}
```

The `FunctionName` attribute marks the method as an Azure Function. This method will be triggered on a time schedule by using the `TimerTrigger` attribute in the parameter of the method. The argument passed into `TimerTrigger` is a CRON expression that instructs the Azure Functions platform when to run the function. This particular CRON expression occurs every 15 seconds.

The Azure Functions platform uses a 6 part CRON syntax:

```
* * * * * *
- - - - - -
| | | | | |
| | | | | +--- day of week (0 - 6) (Sunday=0)
| | | | +----- month (1 - 12)
| | | +------- day of month (1 - 31)
| | +--------- hour (0 - 23)
| +----------- min (0 - 59)
+------------- sec (0 - 59)
```

Azure Functions uses the [NCrontab library](https://github.com/atifaziz/NCrontab) to parse the CRON expressions. You can use this handy [online CRON Expression validation tool](https://ncrontab.swimburger.net/) I made to verify when your Azure Functions will run based on your CRON expression.

The details of the timer are passed into the method through the `myTimer` parameter. The `myTimer` parameter is an example of an input binding. This is a very simple input binding, but input bindings can be a lot more powerful. The Azure Functions platform injects a logger into the method because it is specified as the second parameter of the method. The order of the parameters doesn’t really matter because the platform will use a combination of reflection and dependency injection to configure your Azure Function and inject the necessary parameters.

The method itself only does one thing at the moment: logging a message with the current date and time.

Run the following command to run the Azure Functions project:

```bash
func start
```

Since this is a .NET project, you can use all the usual .NET CLI commands like `clean`, `restore`, `build`, etc., but you cannot run the project directly. You need to use the Azure Functions CLI to run the project as shown above.

The output of running the Azure Functions project should look like this:

```
Microsoft (R) Build Engine version 17.0.0+c9eb9dd64 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.
 
  Determining projects to restore...
  All projects are up-to-date for restore.
  SampleAzureFunctions -> /Users/nswimberghe/SampleAzureFunctions/bin/output/SampleAzureFunctions.dll
 
Build succeeded.
    0 Warning(s)
    0 Error(s)
 
Time Elapsed 00:00:04.22
 
 
 
Azure Functions Core Tools
Core Tools Version: 4.0.3971 Commit hash: d0775d487c93ebd49e9c1166d5c3c01f3c76eaaf (64-bit)
Function Runtime Version: 4.0.1.16815
 
[2022-01-07T21:10:05.720Z] Found /Users/nswimberghe/SampleAzureFunctions/SampleAzureFunctions.csproj. Using for user secrets file configuration.
 
Functions:
 
	SendSmsTimer: timerTrigger
 
For detailed output, run func with --verbose flag.
[2022-01-07T21:10:12.070Z] Host lock lease acquired by instance ID '0000000000000000000000002511DF13'.
[2022-01-07T21:10:15.048Z] Executing 'SendSmsTimer' (Reason='Timer fired at 2022-01-07T16:10:15.0135530-05:00', Id=4098e2d1-5d27-4e5f-bd6f-540eeb3e9d67)
[2022-01-07T21:10:15.059Z] C# Timer trigger function executed at: 1/7/2022 4:10:15 PM
[2022-01-07T21:10:15.083Z] Executed 'SendSmsTimer' (Succeeded, Id=4098e2d1-5d27-4e5f-bd6f-540eeb3e9d67, Duration=54ms)
[2022-01-07T21:10:30.004Z] Executing 'SendSmsTimer' (Reason='Timer fired at 2022-01-07T16:10:30.0032640-05:00', Id=10e74f2c-4e6f-4117-bbba-118a6b595591)
[2022-01-07T21:10:30.004Z] C# Timer trigger function executed at: 1/7/2022 4:10:30 PM
[2022-01-07T21:10:30.005Z] Executed 'SendSmsTimer' (Succeeded, Id=10e74f2c-4e6f-4117-bbba-118a6b595591, Duration=1ms)
```

Every 15 seconds, a message looking like this should be logged: `C# Timer trigger function executed at: 1/7/2022 1:25:00 PM`.

Stop running the project by pressing `control + c`.

Now that you have an Azure Function, you are ready to start integrating Twilio Messaging.

## Integrate Twilio Messaging into your Azure Function

Azure Functions use the concept of triggers, input, and output bindings. You've already encountered a trigger, the `TimerTrigger`, and an input binding of type `TimerInfo`. Now you're going to use your first output binding to send text messages using [Twilio binding](https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-twilio?tabs=csharp).

Add the [Twilio binding NuGet package](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.Twilio) using the .NET CLI:

```bash
dotnet add package Microsoft.Azure.WebJobs.Extensions.Twilio
```

This library is maintained by Microsoft, but the library itself depends on [Twilio's C# SDK](https://github.com/twilio/twilio-csharp) maintained by Twilio.

Update _SendSmsTimer.cs_ with the following code:

```csharp
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
using Twilio.Rest.Api.V2010.Account;
using Twilio.Types;
 
namespace AzureFunctionsWithTwilioBindings
{
    public class SendSmsTimer
    {
        [FunctionName("SendSmsTimer")]
        [return: TwilioSms(
            AccountSidSetting = "TwilioAccountSid",
            AuthTokenSetting = "TwilioAuthToken"
        )]
        public CreateMessageOptions Run([TimerTrigger("\*/15 \* \* \* \* \*")] TimerInfo myTimer, ILogger log)
        {
            log.LogInformation($"SendSmsTimer executed at: {DateTime.Now}");
 
            string toPhoneNumber = Environment.GetEnvironmentVariable("ToPhoneNumber", EnvironmentVariableTarget.Process);
            string fromPhoneNumber = Environment.GetEnvironmentVariable("FromPhoneNumber", EnvironmentVariableTarget.Process);
            var message = new CreateMessageOptions(new PhoneNumber(toPhoneNumber))
            {
                From = new PhoneNumber(fromPhoneNumber),
                Body = "Hello from SendSmsTimer!"
            };
 
            return message;
        }
    }
}
```

With these changes, the `Run` method will now retrieve the recipient and sender phone number from the environment variables, and then create an instance of `CreateMessageOptions` which is then returned. `CreateMessageOptions` lets you specify what phone number the SMS should be sent from, what phone number it should be sent to, and what the contents of the message should be. However, creating this object does not actually send the text message.

The Azure Functions platform will invoke the method, and when it receives the `CreateMessageOptions` instance, the platform will send the SMS. For the platform to be able to send the SMS, it needs to authenticate with your Twilio account. This process is completed by the  `TwilioSms` attribute, from line 13 to 16. This attribute is applied to the **return** type, hence the `return` keyword preceding the attribute. The `AccountSidSetting` and `AuthTokenSetting` parameters are used to specify the names of the application settings that  hold the Twilio Account SID and Twilio Auth Token.With this information, Twilio binding will be able to connect to the Twilio API and send your text messages.

Now, you need to configure the `ToPhoneNumber` and `FromPhoneNumber` environment variables, as well as the `TwilioAccountSid` and `TwilioAuthToken` application settings. To do this, open the _local.settings.json_ file and add all 4 settings to the `Values` object like below:

```bash
{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "FUNCTIONS\_WORKER\_RUNTIME": "dotnet",
        "TwilioAccountSid": "[REPLACE\_WITH\_YOUR\_TWILIO\_ACCOUNT\_SID]", 
        "TwilioAuthToken": "[REPLACE\_WITH\_YOUR\_TWILIO\_AUTH\_TOKEN]",
        "FromPhoneNumber": "[REPLACE\_WITH\_YOUR\_TWILIO\_PHONE\_NUMBER]",
        "ToPhoneNumber": "[REPLACE\_WITH\_RECIPIENT\_PHONE\_NUMBER]"
    }
}
```

Replace the following strings and save the file:

- [REPLACE\_WITH\_YOUR\_TWILIO\_ACCOUNT\_SID] with your Twilio Account SID that you took note of earlier.
- [REPLACE\_WITH\_YOUR\_TWILIO\_AUTH\_TOKEN] with your Twilio Account Auth Token that you took note of earlier.
- [REPLACE\_WITH\_YOUR\_TWILIO\_PHONE\_NUMBER] with the Twilio Phone Number you purchased earlier.
- [REPLACE\_WITH\_RECIPIENT\_PHONE\_NUMBER] with the phone number you want to send text messages to. This phone number has to be a Verified Caller ID as noted earlier if you're using a trial account.

These settings are sensitive, so be very careful not to share them or check them into git, especially the Auth token. Git should ignore the _local.settings.json_ file by default, be careful nonetheless.

The values in the JSON file will be treated as Application Settings and will also be available as **environment variables**.

Start the Azure Function project to test the project:

```bash
func start
```

The Azure Function should be invoked every 15 seconds, which will send an SMS using Twilio.

**But what if you already have another output binding as a return value? Or what if you want to send two different messages?**

As an alternative to returning an instance of `CreateMessageOptions`, you can also add `out` parameters to your method.Here's an updated example:

```csharp
[FunctionName("SendSmsTimer")]
public void Run(
    [TimerTrigger("\*/15 \* \* \* \* \*")]TimerInfo myTimer, 
    ILogger log, 
    [TwilioSms(AccountSidSetting = "TwilioAccountSid",AuthTokenSetting = "TwilioAuthToken")]
    out CreateMessageOptions message1, 
    [TwilioSms(AccountSidSetting = "TwilioAccountSid",AuthTokenSetting = "TwilioAuthToken")]
    out CreateMessageOptions message2
)
{
    log.LogInformation($"SendSmsTimerOut executed at: {DateTime.Now}");
 
    string toPhoneNumber = Environment.GetEnvironmentVariable("ToPhoneNumber", EnvironmentVariableTarget.Process);
    string fromPhoneNumber = Environment.GetEnvironmentVariable("FromPhoneNumber", EnvironmentVariableTarget.Process);
    message1 = new CreateMessageOptions(new PhoneNumber(toPhoneNumber))
    {
        From = new PhoneNumber(fromPhoneNumber),
        Body = "Hello from SendSmsTimerOut message 1!"
    };            
    message2 = new CreateMessageOptions(new PhoneNumber(toPhoneNumber))
    {
        From = new PhoneNumber(fromPhoneNumber),
        Body = "Hello from SendSmsTimerOut message 2!"
    };
}
```

Instead of applying the `TwilioSms` attribute to the return type of the method, the attribute is now applied to the two `out CreateMessageOptions` parameters. Simply assign your `CreateMessageOptions` instance to your `out` parameter and the platform will receive the output and send the text messages.

**How do I send multiple messages without specifying multiple `out` parameters?**

Alternatively, you can use the generic `ICollector<T>` type and `​​IAsyncCollector<T>` type as parameters of your method. The platform will inject an instance of the specified type, to which you can add any amount of instances of the generic type you specified. Here's an example:

```csharp
[FunctionName("SendSmsTimer")]
public void Run(
    [TimerTrigger("\*/15 \* \* \* \* \*")]TimerInfo myTimer, 
    ILogger log, 
    [TwilioSms(AccountSidSetting = "TwilioAccountSid",AuthTokenSetting = "TwilioAuthToken")]
    ICollector<CreateMessageOptions> messageCollector
)
{
    log.LogInformation($"SendMultilpeSmsTimer executed at: {DateTime.Now}");
    
    string toPhoneNumber = Environment.GetEnvironmentVariable("ToPhoneNumber", EnvironmentVariableTarget.Process);
    string fromPhoneNumber = Environment.GetEnvironmentVariable("FromPhoneNumber", EnvironmentVariableTarget.Process);
    for (int i = 1; i <= 2; i++)
    {
        var message = new CreateMessageOptions(new PhoneNumber(toPhoneNumber))
        {
            From = new PhoneNumber(fromPhoneNumber),
            Body = $"Hello from SendMultilpeSmsTimer #{i}!"
        };
        messageCollector.Add(message);
    }
}
```

Now the method accepts an instance of `ICollector<CreateMessageOptions>` to which you can add multiple `CreateMessageOptions` instances. The for loop will add two `CreateMessageOptions` instances and the moment an instance is added using `messageCollector.Add`, the platform will send the text message.

Alternatively, you can use `​​IAsyncCollector` instead of `​ICollector`. The async version will not send text messages immediately, but instead wait for`FlushAsync` to be called. If `FlushAsync` isn’t called, the platform will send your text message after the method is finished.Here's the async version:

```csharp
public async Task Run(
    [TimerTrigger("\*/15 \* \* \* \* \*")]TimerInfo myTimer, 
    ILogger log, 
    [TwilioSms(AccountSidSetting = "TwilioAccountSid",AuthTokenSetting = "TwilioAuthToken")]
    IAsyncCollector<CreateMessageOptions> messageCollector
)
{
    log.LogInformation($"SendMultilpeAsyncSmsTimer executed at: {DateTime.Now}");
    
    string toPhoneNumber = Environment.GetEnvironmentVariable("ToPhoneNumber", EnvironmentVariableTarget.Process);
    string fromPhoneNumber = Environment.GetEnvironmentVariable("FromPhoneNumber", EnvironmentVariableTarget.Process);
    for (int i = 1; i <= 2; i++)
    {
        var message = new CreateMessageOptions(new PhoneNumber(toPhoneNumber))
        {
            From = new PhoneNumber(fromPhoneNumber),
            Body = $"Hello from SendMultilpeAsyncSmsTimer #{i}!"
        };
        await messageCollector.AddAsync(message);
    }
    await messageCollector.FlushAsync();
}
```

Those are all the ways you can send text messages using the Azure Functions’ Twilio binding. Twilio binding is a helpful feature, but if it doesn't fulfill your requirements, you can still use the [Twilio C# SDK](https://www.twilio.com/docs/libraries/csharp-dotnet) directly as with any other .NET application.

## Conclusion

Azure Functions uses the concept of triggers, input, and output bindings. Microsoft maintains Twilio binding for Azure Functions, which helps you send text messages using the Twilio platform. There are multiple ways to send text messages using Azure Functions:

- Return a `CreateMessageOptions` instance
- Assign a `CreateMessageOptions` instance to an `out` parameter
- Let the platform inject an instance of `ICollector<CreateMessageOptions>` or `IAsyncCollector<CreateMessageOptions>` and add instances of `CreateMessageOptions` to it
- Use the [Twilio C# SDK](https://www.twilio.com/docs/libraries/csharp-dotnet)

Twilio has a lot of other products you can integrate into your applications. Check out [this tutorial on how to receive text messages using Twilio Programmable SMS and forward them using **SendGrid** emails with C# and ASP.NET Core](https://www.twilio.com/blog/receive-an-sms-and-email-it-using-twilio-sendgrid-c-and-net-core).

## Additional resources

Check out the following resources for more information on the topics and tools presented in this tutorial:

[SendGrid Binding for Azure Functions](https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-sendgrid?tabs=csharp) – In addition to sending text messages using Twilio binding, you can send emails using the SendGrid binding. 

[Deploy your Azure Function project to Azure](https://docs.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-csharp?tabs=azure-cli%2Cin-process#deploy-the-function-project-to-azure) – Your Azure Functions work locally, but you probably want to deploy them somewhere. Check out the Microsoft Docs to learn how to deploy your Azure Function project to Azure.

[Source Code to this tutorial on GitHub](https://github.com/Swimburger/AzureFunctionsWithTwilioBindings) - Use this source code if you run into any issues, or submit an issue on this GitHub repo if you run into problems.