如何通过Terraform在Azure Marketplace创建Sendgrid订阅
Azure上的Sendgrid属于第三方Marketplace SaaS资源,没有原生Terraform资源可以直接在创建订阅时同步返回访问令牌(即Sendgrid API Key),可以按以下两步落地:
1. 基础配置:通过Terraform创建Sendgrid订阅
首先需要先同意Marketplace对应套餐的服务条款,再创建SaaS订阅资源,参考配置如下(基于azurerm 3.x版本 provider):
terraform { required_providers { azurerm = { source = "hashicorp/azurerm" version = ">=3.0.0" } } } provider "azurerm" { features {} } # 拉取对应Sendgrid套餐的Marketplace协议信息 data "azurerm_marketplace_agreement" "sendgrid" { publisher = "Sendgrid" offer = "sendgrid_azure" plan = "free" # 替换为你实际要采购的套餐标识,比如essential100k、pro100k等 } # 同意服务条款,否则无法创建订阅 resource "azurerm_marketplace_agreement" "sendgrid_accept" { publisher = data.azurerm_marketplace_agreement.sendgrid.publisher offer = data.azurerm_marketplace_agreement.sendgrid.offer plan = data.azurerm_marketplace_agreement.sendgrid.plan license = data.azurerm_marketplace_agreement.sendgrid.license privacy_policy_link = data.azurerm_marketplace_agreement.sendgrid.privacy_policy_link } # 创建部署用资源组 resource "azurerm_resource_group" "sendgrid_rg" { name = "rg-sendgrid-prod" location = "East US" # 选择Sendgrid服务支持的Azure区域 } # 创建Sendgrid SaaS订阅 resource "azurerm_saas_subscription" "sendgrid" { name = "sendgrid-sub-prod" location = azurerm_resource_group.sendgrid_rg.location resource_group_name = azurerm_resource_group.sendgrid_rg.name publisher = "Sendgrid" offer = "sendgrid_azure" plan = "free" # 和前面协议配置的套餐保持一致 sku = "free" term_id = data.azurerm_marketplace_agreement.sendgrid.terms[0].id payment_channel_type = "AzureSubscription" depends_on = [ azurerm_marketplace_agreement.sendgrid_accept ] }
如果不确定自己要选的套餐对应的plan标识,可以在本地安装Azure CLI后执行az vm image list --publisher Sendgrid --offer sendgrid_azure --all查询所有可用套餐的参数。
2. 自动获取Sendgrid访问令牌
Azure侧不会存储Sendgrid侧生成的API Key,所以需要在订阅创建完成后调用Sendgrid自身接口生成令牌,最简便的方式是通过Terraform的local-exec provisioner在资源创建完成后自动执行脚本拉取:
前置一次性操作:首次部署前需要手动进入Azure门户的Sendgrid资源页,完成初始账号激活(设置管理员邮箱、登录密码),后续环境重复部署不需要再做这一步。
在之前的azurerm_saas_subscription.sendgrid资源块内追加以下配置:
# 资源创建完成后自动执行脚本生成API Key provisioner "local-exec" { command = <<EOT # 调用Sendgrid登录接口获取临时会话凭证 LOGIN_RESP=$(curl -s -X POST https://api.sendgrid.com/v3/public/account/login \ -H "Content-Type: application/json" \ -d '{"email":"${var.sendgrid_admin_email}","password":"${var.sendgrid_admin_password}"}') TEMP_TOKEN=$(echo $LOGIN_RESP | jq -r '.token') # 调用API Key创建接口生成指定权限的访问令牌 API_KEY_RESP=$(curl -s -X POST https://api.sendgrid.com/v3/api_keys \ -H "Authorization: Bearer $TEMP_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"terraform-auto-key","scopes":["mail.send"]}') # 按业务需求调整scopes权限范围 # 将生成的密钥写入本地临时文件 echo $(echo $API_KEY_RESP | jq -r '.api_key') > sendgrid_api_key.tmp EOT }
再追加输出相关配置,将生成的密钥作为Terraform输出值使用:
# 读取临时文件中存储的API Key data "local_file" "sendgrid_api_key" { filename = "${path.module}/sendgrid_api_key.tmp" depends_on = [ azurerm_saas_subscription.sendgrid ] } # 输出访问令牌,标记为敏感值避免明文泄露 output "sendgrid_access_token" { value = chomp(data.local_file.sendgrid_api_key.content) sensitive = true } # 管理员账号信息通过敏感变量传入,禁止硬编码在配置文件中 variable "sendgrid_admin_email" { type = string sensitive = true } variable "sendgrid_admin_password" { type = string sensitive = true }
如果是企业级合规场景不允许在Terraform执行节点跑脚本,可以把脚本逻辑迁移到Azure Function/Logic App中,通过SaaS资源创建完成的事件触发执行,最终把密钥存在Azure Key Vault里供业务调用。
避坑说明
- 执行Terraform的机器需要提前安装
curl和jq工具,否则脚本会执行失败 - Sendgrid生成的API Key只会在创建接口返回一次,拿到后要妥善存储,后续无法再通过接口查询同一个Key的明文
- 套餐相关的publisher、offer、plan三个参数必须完全匹配,否则会出现协议校验失败、订阅创建报错的问题
- 不要尝试从Azure资源属性里读取Sendgrid密钥,Azure侧不持有这部分第三方SaaS的业务凭证
内容的提问来源于stack exchange,提问作者Raynigon

