🐢

Azure Communication Services を使って self‑hosted Dify のメール送信を実現

に公開

こんにちは。secondz digital 株式会社でインフラ兼情シスを担当している kame3t です。

はじめに

Self‑hosted で Dify を Azure にデプロイする際、アプリからの通知メールを Azure Communication Services (ACS) の Email 機能で送信できるようにしたので、その手順を共有します。
メール送信は Dify の必須要件ではありませんが、招待メール・パスワードリセット・ワークフローのデバッグ などであると便利です。


ゴール

  • Azure だけで完結した SMTP リレーを構築する
  • Terraform で 再現可能 な IaC を提供する
  • Swaks でメール送信を検証する

対象読者は Terraform と Azure の基礎を理解している方 を想定しています。


2025 年 5 月時点の選択肢

Dify が公式にサポートするメール送信方法は次の 2 つです。

方法 特徴 コメント
SMTP おなじみの標準プロトコル 既存 SMTP サーバがある場合は簡単
Resend 管理不要なメール API 従量課金・外部サービスへの依存

社内 PoC 用に できるだけ Azure 内で閉じたい という要件から、ACS Email を SMTP エンドポイント として利用する構成を選択しました。


全体構成


Terraform モジュール

以下は最小構成の例です。azurerm provider は v4.28 で検証しています。

terraform {
  required_version = ">= 1.10.0"

  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.28"
    }
    azuread = {
      source  = "hashicorp/azuread"
      version = "~> 3.3"
    }
  }
}

provider "azurerm" {
  features {}
}

# ---------- Azure AD アプリ / SP ----------
resource "azuread_application" "smtp_app" {
  display_name     = "dify-smtp-app"
  sign_in_audience = "AzureADMyOrg"
}

resource "azuread_service_principal" "smtp_sp" {
  client_id = azuread_application.smtp_app.client_id
}

resource "azuread_service_principal_password" "smtp_sp_pwd" {
  service_principal_id = azuread_service_principal.smtp_sp.id
  # 有効期限は要件に応じて設定
  end_date_relative = "8760h" # 1 年
}

# ---------- Communication Services ----------
resource "azurerm_communication_service" "comm" {
  name                = "comm-svc"
  resource_group_name = azurerm_resource_group.rg.name
  data_location       = var.location
}

resource "azurerm_email_communication_service" "email" {
  name                = "email-svc"
  resource_group_name = azurerm_resource_group.rg.name
  data_location       = azurerm_communication_service.comm.data_location
}

# マネージドドメイン (DoNotReply@xxxxx.azurecomm.net)
resource "azurerm_email_communication_service_domain" "managed" {
  name              = "AzureManagedDomain"
  email_service_id  = azurerm_email_communication_service.email.id
  domain_management = "AzureManaged"
}

# Communication Service と Email ドメインの関連付け
resource "azurerm_communication_service_email_domain_association" "assoc" {
  communication_service_id = azurerm_communication_service.comm.id
  email_service_domain_id  = azurerm_email_communication_service_domain.managed.id
}

# ---------- 役割定義 & 付与 ----------
resource "azurerm_role_definition" "dify_email_sender" {
  name  = "DifyEmailSender"
  scope = data.azurerm_subscription.current.id

  permissions {
    actions = [
      "Microsoft.Communication/CommunicationServices/Read",
      "Microsoft.Communication/CommunicationServices/EmailServices/Write",
      "Microsoft.Communication/CommunicationServices/EmailServices/Read"
    ]
  }

  assignable_scopes = [data.azurerm_subscription.current.id]
}

resource "azurerm_role_assignment" "dify_email_sender" {
  scope              = azurerm_communication_service.comm.id
  role_definition_id = azurerm_role_definition.dify_email_sender.role_definition_resource_id
  principal_id       = azuread_service_principal.smtp_sp.object_id
  depends_on         = [azurerm_role_definition.dify_email_sender]
}

SMTP 接続情報 (Terraform locals)

locals {
  smtp_host     = "smtp.azurecomm.net"
  smtp_port     = 587

  # <CommunicationServiceName>|<AppId>|<TenantId>
  smtp_username = "${azurerm_communication_service.comm.name}|${azuread_service_principal.smtp_sp.client_id}|${data.azuread_client_config.current.tenant_id}"
  smtp_password = azuread_service_principal_password.smtp_sp_pwd.value

  # 送信元アドレス (必ず DoNotReply@...)
  mail_from     = "DoNotReply@${azurerm_email_communication_service_domain.managed.from_sender_domain}"
}

Dify の環境変数

#--- メール設定 --------------------------
MAIL_TYPE              = "smtp"
SMTP_SERVER            = "${local.smtp_host}"
SMTP_PORT              = "${local.smtp_port}"
SMTP_USERNAME          = "${local.smtp_username}"
SMTP_PASSWORD          = "${local.smtp_password}"  # ← 機密管理を推奨
SMTP_USE_TLS           = "true"
SMTP_OPPORTUNISTIC_TLS = "true"
MAIL_DEFAULT_SEND_FROM = "${local.mail_from}"

動作確認 (Swaks)

swaks \
  --to you@example.com \
  --from DoNotReply@${local.mail_from} \
  --server ${local.smtp_host}:${local.smtp_port} \
  --auth LOGIN \
  --auth-user "${local.smtp_username}" \
  --auth-password "${local.smtp_password}" \
  --header "Subject: ACS SMTP テスト" \
  --body "こんにちは、Dify からのテストメールです。" \
  --tls

ハマりポイント
MAIL_DEFAULT_SEND_FROMDoNotReply@… 以外にするとエラーで拒否されます。ACS のマネージドドメインを使う場合は必ず既定のアドレスを使用しましょう。


まとめ

  • ACS Email を使えば追加コストなく SMTP リレーを用意できる
  • サービスプリンシパルを使って資格情報を一元管理可能
  • DoNotReply@ 以外の From アドレスを使う場合は カスタムドメインの検証 が別途必要

Dify でメールを有効化すると、ユーザー招待やワークフローのテストがとてもスムーズになります。
ぜひ試してみてください!


参考リンク

Discussion