Azure Functions × .NET でメールを送信する

Azure FunctionsはAzureで提供されているFaaS(Function As a Service)です。複数の言語に対応していますが、Microsoft製のサービスとあって他にはない.NETが使えるのがポイントです。

今回はこのAzure Functionsを使ってCustomers Mail Cloudでメール送信を行う方法を紹介します。

Customers Mail Cloudの設定

まずはCustomers Mail Cloudにて送信サーバの設定を行います。

ユーザ登録

ユーザ登録はまず、メールアドレスとパスワードを入力するところからはじまります。

f:id:moongift:20190510151017p:plain
ユーザ登録

入力したメールアドレス宛にメールアドレスの確認メール(仮登録受付メール)が届きますので、URLをクリックします。その後、名前や住所といった必要な情報を入力します。ユーザ登録が完了すると、無料トライアル開始のための審査が入ります。時間は長くとも30分程度です。「Customers Mail Cloud 無料トライアル利用開始のご案内」というメールが届いたら利用できます。

ログイン後に行うこと

ログインしたら、2つの作業を行う必要があります。

f:id:moongift:20190510150911p:plain
ダッシュボード

DKIMキーを設定する

DKIMキーはメールのなりすましを防止するための技術になります。メール配信を行うドメインのTXTレコードに対して設定します。管理コンソールでドメインを追加しようとすると、専用のキーが表示されます。

f:id:moongift:20190510150735p:plain
DKIMキーの取得

ドメインは s999999999999._domainkey.example.com のような形式になります。設定する値は v=DKIM1; p=MIG...QAB のように指定されるはずです。この設定はシステム管理者などに依頼して行ってもらってください。

DNS設定はすぐに反映されませんので、数分後にDNSを確認ボタンを押します。正しく設定されていれば、ドメインが追加登録できます。

ユーザを追加する

次にメール配信を行うユーザを追加します。これはAPI設定にて行います。ユーザ名(メールアドレス形式)、パスワードを設定します。また、利用できる機能を制限できます。例えばSMTPのみ、APIのみといった指定も可能です。

f:id:moongift:20190510150841p:plain
ユーザの追加

サーバに設定を反映する

設定を行ったら、それをサーバに反映します。サーバ構成機能にて行います。この反映を行わないと、DKIMやユーザ追加設定が反映されませんので注意してください。ここまでで以下の情報が入手できているはずです。

  • APIユーザ
  • APIキー

さらにAPIサーバのエンドポイントURLが必要です。これは契約しているプランによって異なりますのでご注意ください。

プラン名 エンドポイントURL
無料トライアル https://sandbox.smtps.jp/api/v2/emails/send.json
Standardプラン https://te.smtps.jp/api/v2/emails/send.json
Proプラン https://SUBDOMAIN.smtps.jp/api/v2/emails/send.json

これらの情報はメール送信時に設定しますので覚えておいてください。

AzureでFunctionsのベースを作成する

AzureにログインしてFunction Appを選択し、ベースを作成します。今回はWindowsで、.NET Coreを選択しています。

f:id:moongift:20190808172310p:plain
AzureでFunctionsを作成

CLIツールのインストール

Azure用のCLIツールをインストールします。インストールはAzure CLI のインストールに各OS向けの説明があります。macOSの場合、Homebrewを使って下記コマンドでインストールできます。

brew update && brew install azure-cli

プロジェクトの作成

以下のコマンドでAzureにログインします。

az login

ログイン後、プロジェクトのベースを作成します。対話型になっているので、言語としてdotnetを選ぶのを忘れないでください。

$ func init azure-function
Select a worker runtime: 
1. dotnet
2. node
3. python (preview)
4. powershell (preview)
Choose option: 1
dotnet

Writing /path/to/azure-function/.vscode/extensions.json

プロジェクトは以下のような構成になっています。まだ空の状態と言えます。

$ tree .
.
├── azure-function.csproj
├── host.json
└── local.settings.json

0 directories, 3 files

そしてテンプレートを使ってC#のファイルを作成します。今回はHTTPアクセスによる関数実行にするため、HttpTriggerをテンプレートにしています。

$ func new --name MyHttpTrigger --template "HttpTrigger"
Select a template: Function name: MyHttpTrigger

The function "MyHttpTrigger" was created successfully from the "HttpTrigger" template.

環境設定を行う

API経由でメール送信を行う際に使うAPIユーザ、APIキー、さらに契約状態によって異なるAPIエンドポイントURLは環境変数として定義します。開発時には local.settings.json に書き込みます。

{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "",
        "FUNCTIONS_WORKER_RUNTIME": "dotnet",
        "API_USER": "APIユーザ名",
        "API_KEY": "APIキー",
        "ENDPOINT": "エンドポイントのURL"
    }
}

これはローカルでの設定になります。本番環境側では、Azure Functionsの構成にある、アプリケーション設定で値を追加します。値の名前は、 local.settings.json と同じものです。

  • API_USER
  • API_KEY
  • ENDPOINT

f:id:moongift:20190808172358p:plain
アプリケーション設定

コードについて

MyHttpTrigger.cs について解説します。まず必要なライブラリを読み込みます。

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;

次にメール送信APIに必要なパラメータをクラスで定義します。

[DataContract]
public class Email {
  [DataMember(Name="name")]
  public string Name { get; set; }
  [DataMember(Name="address")]
  public string Address { get; set; }
}

[DataContract]
public class Params {
  [DataMember(Name="api_user")]
  public string ApiUser {get; set; }
  [DataMember(Name="api_key")]
  public string ApiKey {get; set; }
  [DataMember(Name="subject")]
  public string Subject {get; set; }
  [DataMember(Name="text")]
  public string Text {get; set; }
  [DataMember(Name="to")]
  public Email[] To {get; set;}
  [DataMember(Name="from")]
  public Email From {get; set;}
}

環境変数を読み込むため、Run関数に ExecutionContext context を追加します。

public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
    ILogger log,
    ExecutionContext context
    )

ここからの処理は、このRun関数の中に記述していきます。まずリクエストデータ(POST)を処理できるようにします。

string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);

環境変数を読み込みます。これで config として扱えるようになります。設定ファイルの後、環境変数も読み込みますので、ローカルの開発時と本番環境で同じ config からアクセスできるようになります。

var config = new ConfigurationBuilder()
  .SetBasePath(context.FunctionAppDirectory)
  .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
  .AddEnvironmentVariables()
  .Build();

メール送信APIに送信するパラメータを作ります。APIに関する情報は config から、メールの送信先はリクエストボディから指定できるようにしました。

Params p = new Params();
p.ApiUser = config["API_USER"];
p.ApiKey = config["API_KEY"];
p.Subject = "テストメール from Customers Mail Cloud";
p.Text = "こんにちは。\r\n\r\n改行を入れました。";
Email to = new Email();
to.Name = data?.name;
to.Address = data?.address;
p.To = new Email[] {to};
Email from = new Email();
from.Name = "Admin";
from.Address = "info@smtpd.jp";
p.From = from;

そして、このパラメータをJSON文字列にします。

var ms = new MemoryStream();
var sr = new StreamReader(ms);
var serializer = new DataContractJsonSerializer(typeof(Params));
serializer.WriteObject(ms, p);
ms.Position = 0;
var json = sr.ReadToEnd();

そしてメール送信APIを呼び出します。この処理は C#でメールを送信する で書いている通りです。

System.Net.WebClient wc = new System.Net.WebClient();
wc.Headers[System.Net.HttpRequestHeader.ContentType] = "application/json;charset=UTF-8";
wc.Headers[System.Net.HttpRequestHeader.Accept] = "application/json";
wc.Encoding = System.Text.Encoding.UTF8;
string response = wc.UploadString(new Uri(config["ENDPOINT"]), json);
wc.Dispose();
return (ActionResult)new OkObjectResult(response);

ローカルでテストする

Azure CLIではローカルでテストできる仕組みがあります。

func host start --build

そうすると最後に次のように出力されます。

Http Functions:

    MyHttpTrigger: [GET,POST] http://localhost:7071/api/MyHttpTrigger

このURLに対してリクエストを行うことでテスト実行が可能です。

$ curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"name":"テストカスタマー", "email":"user@stmpd.jp"}' \
  http://localhost:7071/api/MyHttpTrigger

デプロイする

Azure Functionsへのデプロイは以下のコマンドで行えます。最後の引数はあらかじめ作成してあるAzure Function Appの名前です。

$ func azure functionapp publish CustomersMailCloud
.NET Core 向け Microsoft (R) Build Engine バージョン 15.9.20+g88f5fadfbe
Copyright (C) Microsoft Corporation.All rights reserved.
  :
Getting site publishing info...
Creating archive for current directory...
Uploading 3.37 MB [###############################################################################]
Upload completed successfully.
Deployment completed successfully.
Syncing triggers...
Functions in CustomersMailCloud:
    MyHttpTrigger - [httpTrigger]
        Invoke url: https://customersmailcloud.azurewebsites.net/api/example?code=3hy...4pw==

初回実行時に専用のURLが生成されます。上記の場合は https://customersmailcloud.azurewebsites.net/api/example?code=3hy...4pw== となります。このURLは管理画面でも確認できます。

メール送信を試す

では実際にメールを送信してみます。

$ curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"name":"テストカスタマー", "address":"user@smtpd.jp"}' \
  https://customersmailcloud.azurewebsites.net/api/example?code=3hy...4pw==

問題なければ以下のように結果が返ってきます。

{"id":"C1673412149.1272.1565250534682@mta01.sandbox.smtps.jp"}

まとめ

Azure Functionsを使うことでサーバレスでC#(またはNode.jsやPythonなど)を実行できます。さらにCustomers Mail Cloudと組み合わせることで、サーバレスでのメール送信が簡単に実現できます。ぜひお試しください!

クラウドからのメール送信を簡単に。確実に。| Customers Mail Cloud