Azureでもドメインが買える

CloudflareやAWSでドメインを買っている人が最近は多いイメージですが、 Microsoft Azureでも「App Service ドメイン」でドメインの購入をすることができます。
サービス名に「App Service」が入っていることから1、サーバレスアプリケーション等でしか利用できないように見えますが、 別にそんなことはなくAzure VMとかに割当をしたり、Azure DNSサービス(AWSで言うRoute53)も付帯するので、DNSレコードを設定すればAzureに関係のないものでも利用できます。

Azureポータルだと共同作成者はドメインを買わせてくれない

検証用にドメインが1つ欲しいけどわざわざドメイン取るのも面倒だからAzureでドメイン買うたろ、 と思ったので画面でポチポチしてたんですが、いざ作成しようとするとこんなエラーが。

Deployment failed with multiple errors:
'Authorization failed for template resource 'contoso.com/Microsoft.Authorization/contoso.com'
of type 'Microsoft.DomainRegistration/domains/providers/locks'.
The client 'user@contoso.com' with object id '00000000-0000-0000-0000-000000000000'
does not have permission to perform action 'Microsoft.Authorization/locks/write'
at scope '/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/rg-example-001/providers/Microsoft.DomainRegistration/domains/contoso.com/providers/Microsoft.Authorization/locks/contoso.com

ドメインということで誤ってリソースを削除しないためにロックが自動で掛かるみたいです。 Azureの仕様上、「共同作成者(Contributor)」ロールではリソースの作成はできても、リソースロックの操作(作成・削除)ができません。 ロックを操作するには「ユーザーアクセス管理者」や「所有者」といったより強力な権限が必要です。 今回触っているのが自己所有のテナントじゃないので、設定を依頼するのも面倒だな・・・ということでなんとかならんか試してみることに。

※本番環境等でロックが必要な場合は、ちゃんと権限があるえらい人に依頼してください

ARMテンプレートを作ってみる

エラーになった画面でARMテンプレートを作成して、「カスタムテンプレートからのデプロイ」の画面にコピペします(たぶんこれが一番早い)。

やった内容は下記。

  • ドメインに対するロックと、DNSに対するロックを作成しない
  • ついでにパラメータ内で利用しない JobTitle (役職)、 NameMiddle (ミドルネーム)を必須入力から外す
    • 場合によっては Address2 (住所2)も外してもいいかも
{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "subscriptionId": {
            "type": "string"
        },
        "domainName": {
            "type": "string"
        },
        "resourceGroup": {
            "type": "string"
        },
        "agreementKeys": {
            "type": "array"
        },
        "AgreedBy": {
            "type": "string"
        },
        "AgreedAt": {
            "type": "string"
        },
        "Address1": {
            "type": "string"
        },
        "Address2": {
            "type": "string"
        },
        "City": {
            "type": "string"
        },
        "Country": {
            "type": "string"
        },
        "PostalCode": {
            "type": "string"
        },
        "State": {
            "type": "string"
        },
        "Email": {
            "type": "string"
        },
        "JobTitle": {
            "type": "string",
+           "defaultValue": ""
        },
        "NameFirst": {
            "type": "string"
        },
        "NameLast": {
            "type": "string"
        },
        "NameMiddle": {
            "type": "string",
+           "defaultValue": ""
        },
        "Organization": {
            "type": "string"
        },
        "Phone": {
            "type": "string"
        },
        "autoRenew": {
            "type": "bool"
        },
        "privacy": {
            "type": "bool"
        }
    }
    "resources": [
        {
            "apiVersion": "2022-03-01",
            "name": "[parameters('domainName')]",
            "type": "Microsoft.DomainRegistration/domains",
            "location": "global",
            "tags": null,
            "dependsOn": [
                "Microsoft.Network/dnszones/"
            ],
            "properties": {
                "Consent": {
                    "AgreementKeys": "[parameters('agreementKeys')]",
                    "AgreedBy": "[parameters('AgreedBy')]",
                    "AgreedAt": "[parameters('AgreedAt')]"
                },
                "privacy": "[parameters('privacy')]",
                "autoRenew": "[parameters('autoRenew')]",
                "targetDnsType": "AzureDns",
                "dnsZoneId": "[concat('/subscriptions/', parameters('subscriptionId'),'/resourcegroups/', parameters('resourceGroup'), '/providers/Microsoft.Network/dnszones/', parameters('domainName'))]"
            }
        },
-         {
-             "type": "Microsoft.DomainRegistration/domains/providers/locks",
-             "name": "/Microsoft.Authorization/",
-             "apiVersion": "2017-04-01",
-             "dependsOn": [
-                 "Microsoft.DomainRegistration/domains/"
-             ],
-             "properties": {
-                 "level": "CannotDelete",
-                 "notes": "ドメインを削除すると、そのドメインが 60 日間にわたって購入の対象にできなくなります。このドメインを削除する前に、ロックを解除してください。"
-             }
-         },
        {
            "type": "Microsoft.Network/dnszones",
            "name": "",
            "apiVersion": "2018-05-01",
            "location": "global",
            "properties": {}
        },
-       {
-           "type": "Microsoft.Network/dnszones/providers/locks",
-           "name": "/Microsoft.Authorization/",
-           "apiVersion": "2017-04-01",
-           "dependsOn": [
-               "Microsoft.Network/dnszones/"
-           ],
-           "properties": {
-               "level": "CannotDelete",
-               "notes": "この DNS ゾーンはドメインを購入するときに作成され、依然としてドメインで必要とされています。それでもこの DNS ゾーンを削除する場合は、ロックを解除してからゾーンを削除してください。"
-           }
        }
    ]
}

見知らぬパラメータの存在に気付く

これで作れるな!と意気揚々と続行すると、用途のわからないパラメータが・・・

  • Agreement Keys
  • Agreed By
  • Agreed At

よくわからんし空欄でいいやと思ってパラメータに "defaultValue": "" を指定したら当然チェックでこけました。

App Service Domainは、裏側でGoDaddyなどのレジストラを通じてドメインを購入しているので、ICANNの利用規約に「同意した」という法的な証拠をパラメーターとして渡す必要があるとのこと。

ということで調べまくった結果正しそうなパラメータは下記でした。
本当にこれでいいのかはわかりませんが、ドメインは取れたのできっと大丈夫でしょう。
この記事を参考にして実際にやる人はもっとちゃんと調べてからやるのをお勧めします。

  • Agreement Keys : ["DNRA"],["DNPA"]
  • Agreed By : 確認者のメールアドレス
  • Agreed At : 確認日時をISO8601形式で記載
    • 2026-03-01T00:00:00Z などなど

ということで、実際に作成できた時のパラメータはこんな感じ。

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "subscriptionId": {
      "value": "(subscriptionId)"
    },
    "domainName": {
      "value": "contoso.com"
    },
    "resourceGroup": {
      "value": "(resourceGroupName)"
    },
    "agreementKeys": {
      "value": [
        "DNRA",
        "DNPA"
      ]
    },
    "AgreedBy": {
      "value": "webmaster@contoso.com"
    },
    "AgreedAt": {
      "value": "2026-03-05T13:40:00Z"
    },
    "Address1": {
      "value": "2-16-3"
    },
    "Address2": {
      "value": "Shinagawa Grand Central Tower"
    },
    "City": {
      "value": "Konan, Minato-ku"
    },
    "Country": {
      "value": "JP" // ISO 3166-1 alpha-2で定義された国コード
    },
    "PostalCode": {
      "value": "108-0075"
    },
    "State": {
      "value": "Tokyo" // Tokyo-to と入れたら怒られた
    },
    "Email": {
      "value": "webmaster@contoso.com"
    },
    "JobTitle": {
      "value": null
    },
    "NameFirst": {
      "value": "Bill"
    },
    "NameLast": {
      "value": "Gates"
    },
    "NameMiddle": {
      "value": null
    },
    "Organization": {
      "value": "Contoso Corporation"
    },
    "Phone": {
      "value": "+81.03 4332 5300" // 国番号の後ろにドットが必要
    },
    "autoRenew": {
      "value": true // 自動更新が有効
    },
    "privacy": {
      "value": true // プライバシーガード(WHOIS非公開)が有効
    }
  }
}

まとめ

Azureを使うときは強い権限があるとよくわからないところで詰まなくて済みます。
(資格所有者が言っていい台詞ではない)


参考


  1. てっきりこれ調べるまでApp Service専用だと思ってた ↩︎