Azure for AWS engineers: AWSの勘がどこで通用し、どこで通用しないか
AWSでアカウントを整理し、権限を付与し、ステートをブートストラップする方法は既にご存知でしょう。本記事では、Azureで同じことを行う方法、明確にマッピングされる概念、そしてAWSの経験が誤解を招く4つのポイントを解説します。
AWSに精通している方がAzureに目を向けた場合、朗報としては、その直感の約80%が直接適用できるということです。つまり、物事を整理するための階層があり、アクセスを許可するためのロールベースの方法があり、キーレスCIのためのOIDCの仕組みがあり、「ステートバックエンドをコールドスタートする」ブートストラップの手順があります。悪いニュースは残りの20%です。それは、AWSのやり方が混乱を招く拒否につながり、明確なエラーにならないような、使用頻度の高い特定の箇所に集中しています。本記事は、変換レイヤーとして、AWSで行うのと同じタスクをAzureで行う方法と、その落とし穴を指摘します。
もし1段落だけ読むなら、要するに次のとおりです。AWSには基本的に1つの権限プレーン(IAM)がありますが、Azureには互いに連携しない3つのプレーンがあります。AWSアカウントはAzureサブスクリプションに相当しますが、請求は全く別の場所に存在します。そして、AzureにはAWSには真に同等のものがない必須のコンテナ(リソースグループ)が追加されます。 以下では、これらを詳しく説明します。
最大の変化:1つの権限プレーンが3つになる
AWSでは、IAMが事実上すべての役割を担っています。1つのサービスが、IDが何であるか、IDがリソースに対して何ができるか、そしてOrganizationsを通じてアカウントがどのように作成されグループ化されるかを統制します。権限が統合されていることは、それが失われるまでおそらく気づかないでしょう。
Azureは、その単一のプレーンを意図的に3つの独立したプレーンに分割しており、それぞれに3つのロールシステム、3つの付与メカニズムがあり、自動的な透過性はほとんどありません。1つのプレーンで全能であっても、他のプレーンでは何もできません。これは、ほとんどすべての「管理者なのに、なぜこれが拒否されるのか?」という瞬間の原因となるため、最も重要な理解すべき点です。
あなたを救うルールは次のとおりです。Azureが「動くはず」のものを拒否した場合、まず「どのプレーンに話しているのか?」と問いかけ、次にそのプレーンのロールを確認してください。 ほとんどの謎の拒否(サブスクリプションを読み取れない、管理グループを一覧表示できない、請求が見えないなど)は、プレーン内の権限の不足ではなく、プレーン自体を間違えていることが原因です。
プレーン1 — Entra ID ディレクトリロール (ID)
このプレーンは、ディレクトリオブジェクト、つまりユーザー、グループ、サービスプリンシパル/アプリ登録、Conditional Access、MFAポリシー、ライセンスを統制します。展開するリソースは統制しません。
- ここでのロールは、Global Administrator、User Administrator、Application Administratorなどです。これらはEntra ID内で割り当てられ評価され、Microsoft Graph APIを通じて公開されます。
- Global Administratorはディレクトリの神であり、リソースの神ではありません。 これは誰もが戸惑う点です。Global Adminは、組織内のすべてのユーザーとアプリを管理できますが、サブスクリプションを単に読み取ろうとしただけで承認エラーになることがあります。最も近いAWSの類推は「IAM Identity Centerとディレクトリ自体を管理する人」ですが、AWSがディレクトリ管理とリソース認可を融合させているのに対し、Azureはそれを拒否しているため、不完全です。
プレーン2 — Azure RBAC (リソース)
これは、azurerm Terraformプロバイダーが駆動する、あなたが活動するプレーンです。VM、仮想ネットワーク、ストレージ、AKSなど、展開するすべてのリソースを統制します。
- 割り当ては3つの要素で構成されます。(プリンシパル、ロール定義、スコープ) スコープは
root → management group → subscription → resource group → resourceというチェーンのノードであり、下位に継承されます。組み込みロールには、Owner、Contributor、Readerがあり、これに加えてカスタムロール定義を作成できます。 - ここがAWS脳の落とし穴です。IDにアタッチされたポリシーはありません。 AWSでは、ポリシーをユーザーまたはロールにアタッチし、権限がIDと共に移動します。Azureでは、スコープにおけるロールが唯一のモデルです。AWSにおける最も近い精神的形態は「Organizations OUにアタッチされたIAMポリシー」です。つまり、権限はプリンシパルではなくツリーノードに存在します。
- プレーン1と2の間の唯一の正式に認められたブリッジは、意図的な緊急時対応措置です。Global Administratorは「Azureリソースのアクセス管理」(
elevateAccess操作)を切り替えることで、自身にルートスコープでのUser Access Administratorを付与できます。これは明示的で、元に戻すことができ、デフォルトではありません。ブートストラップ時に一度使用して、テナントルートで自身にOwnerを割り当て、それがすべてのサブスクリプションに継承されます。
プレーン3 — 請求 / コマース (費用)
このプレーンは、請求アカウント、請求プロファイル、インボイスセクション、支払い方法、そして重要なサブスクリプションの作成を統制します。
- 独自のロールセット(Billing account owner、Billing profile owner、Azure subscription creatorなど)があり、請求アカウント内で付与され、請求システムに保存されます。これらのロールは
az role assignment listやEntraのロールブレードには表示されません。これらは完全に独立した世界です。 - この壁には両方向からぶつかる可能性があります。最大限のID権限 + リソース権限(Global Admin + ルートでのUser Access Administrator + ルート管理グループでのOwner)があっても、
az billing account listから空のリストが返され、サブスクリプションを作成する能力はありません。逆に、請求オーナーはワンクリックで請求権限を付与できます。 - 最も巧妙な点は、請求はARM風のREST URL(
Microsoft.Billing/...)を通じて提供されるため、リソースプレーンのように見えますが、承認は請求ロールに対して評価されます。同じドアでも門番が違います。
AWSでは決して考える必要がなかった具体的な結果として、プログラムでサブスクリプションを作成できるかどうかは、請求契約の種類によって異なります。 従来の従量課金/Web直接契約アカウントでは、ポータルで最初のサインアップIDによって手動でしかサブスクリプションを作成できません。その所有権は付与することさえできません。現代のCustomer Agreementは、サブスクリプション作成API(セルフサービスアカウントでは小売上限があり、合計で数個のサブスクリプションと日ごとのレート制限)をサポートしており、エンタープライズ/パートナー契約では上限が撤廃されます。AWSでは、管理アカウントからCreateAccountを実行するだけで機能します。Azureでは、「このアカウントをコードで作成できるか?」は、まず答えるべき請求プレーンの質問です。そのため、多くのAzure環境では、サブスクリプションはTerraformにインポートされるものであり、Terraformによって作成されるものではありません。
実際におけるプレーンの交差
ほとんどのタスクは正確に1つのプレーンに触れますが、いくつかのタスクは複数のプレーンにまたがります。これが、分割が重要である理由です。
- ユーザー、グループ、またはアプリ登録の作成 → Entraのみ。
- VM / VNet / ストレージアカウントのデプロイ → リソース(Azure RBAC)のみ。
- サブスクリプションの作成 → 請求によって作成され、Entraテナントに属し、Azure RBACスコープになります。1つのアクションに対して3つのプレーンが関わります。
- ディレクトリ監査ログをLog Analyticsワークスペースにエクスポート → Entra(ソース)とリソース(宛先)。
- Terraform
azurermvsazuread→ リソースAPI vs Graph API — 異なるエンドポイント、異なるトークンオーディエンス。
最後の点は実際の運用上の問題を引き起こします。単一のログインはオーディエンスごとに個別のトークン(リソースマネージャー用、Graph用、Key Vault用など)を発行します。あるプレーンのAPI用に発行されたトークンは、別のプレーンでは無効です。ツールが1つしか取得しない場合、Terraformの半分が不思議なことに401エラーになります。
「Entra ID」、「tenant」、「directory」は(ほとんど)同じもの
3つの言葉は、異なる角度から見た1つのオブジェクトを指し、さらに混乱させるための名称変更も伴います。
- Entra IDは製品、つまりIDサービスです。2023年まではAzure Active Directory (Azure AD / AAD) であり、古い名前は
azureadTerraformプロバイダー、AADSTS…エラーコード、ドキュメントの「AAD auth」など、いたるところに残っています。同じものです。 - テナントはEntra IDの組織専用インスタンスです。GUIDとプライマリドメインによって識別されるコンテナであり、信頼/分離境界です。ユーザー、グループ、アプリ、Conditional Access、ライセンスはすべてテナントごとに存在し、テナントを越えることはありません。
- ディレクトリはテナントのコンテンツ、つまりIDオブジェクトのデータベースです。1つのテナントは1つのディレクトリに等しいため、人々はこれらの言葉を同じ意味で使います。ポータルの「ディレクトリの切り替え」ボタンは、実際にはテナントを切り替えます。
以下のルールは、AWSとの類推が曖昧になる部分です。
- サブスクリプションは正確に1つのテナントに属します。 そのテナントが認証レルムとなり、そのRBACプリンシパルを提供します。(サブスクリプションはテナント間で転送可能ですが、請求は別の関連付けです。)
- 1つの組織が複数のテナントを所有できます。 一般的なパターンは、ロックダウンされた本番テナントと、完全に分離されたサンドボックステナントです。これらは、一方で行うことが他方に影響しない、独立したIDの世界です。同じ会社の下に「完全に独立した2つ目のIDユニバース」というAWSの同等の概念はありません。
- ユーザーは1つのホームテナントを持ち、他の場所にゲスト(B2B)として参加できます。 ゲストは、ゲストテナント内でローカルのオブジェクトIDを取得しますが、ホームIDを保持します。AWSには「別の組織のディレクトリからのゲストユーザー」というファーストクラスの概念はありません。
最も曖昧なAWSの類推としては、テナントはおおよそ「Identity Centerディレクトリと融合したAWS Organization」 と言えます。ただし、サブスクリプションはIDのためにのみテナントにアタッチされ、支払いは請求アカウントにアタッチされ、組織間ゲストはネイティブな概念です。
リソースグループ:AWSにはないコンテナ
リソースグループ (RG) はサブスクリプション内の必須コンテナであり、すべてのリソースは正確に1つのRGに存在します。これはAWSには同等のものがない部分です(AWSの「リソースグループ」は単なる保存されたタグクエリであり、名前の衝突は無視してください)。
RGは同時に4つの役割を担います。
- RBACスコープ — 1つのRGに対してReader権限を付与すると、そのワークロードにのみアクセスが制限されます。
- ポリシー適用範囲 — RGレベルでガバナンスルールをアタッチできます。
- ライフサイクル単位 — RGを削除すると、その中のすべてのリソースも削除されます。
- コスト境界 — 費用を項目分けするための自然な線引きです。
一般的なパターンは、ワークロードごと(多くの場合、リージョンごと)に1つのRGを使用することであり、RGは「このリージョンのこのアプリケーションのフォルダ」となります。RGには場所がありますが、それはそのメタデータがどこに存在するかを示すだけであり、そのリソースは他のリージョンに存在することもできるため、RGの場所に過度に注目しないでください。
リソースIDがコンテキストを持つため、名前にその必要がない
すべてのAzureリソースには、/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.App/containerApps/<name>のような完全なリソースIDがあります。サブスクリプションとRGは、すべてのログレコード、監査イベント、API呼び出しと共に伝達されます。これは、AWSのARNにおけるアカウントとリージョンの役割と同じです。
命名の観点からは、AWSの習慣とは逆の結果になります。AWSでは、名前が唯一のコンテキストであることが多いため、すべてを名前に詰め込みます。Azureでは、名前はスコープが既にエンコードしているものを省略するべきです。qaサブスクリプション内のrg-quest内のaks-questというクラスターは、すでに完全に曖昧さ解消されており、同じRG/リソース名を開発/QA/本番サブスクリプション間で安全に繰り返すことができます。ストレージアカウント、コンテナーレジストリ、Key Vaultのようなグローバルに一意なリソースタイプのみが、組織/環境/リージョンを名前に戻すことを強制しますが、これらには厳密な長さと文字の制限があるため、stcwtfstateplatformcusのような圧縮された名前が見られます。
(ちなみに、このcusサフィックスは造語ではなく、Microsoft独自の米国中部(Central US)の地域コードであり、AzureがプライベートエンドポイントのDNSゾーンを構築するために使用するのと同じ公式テーブルから来ています。eus/eus2はそれぞれ米国東部(East US)と米国東部2(East US 2)を指します。地域コードテーブルを早めに学ぶことは非常に役立ちます。)
AWS → Azure クイックマップ
最初の1ヶ月間は、これを手元に置いておいてください。
- Organization → Entraのテナント と 管理グループ。ID(テナント)と構造(管理グループ)はAzureでは別のものです。
- Account → サブスクリプション。IDのためにテナントに属し、別の請求アカウントによって_請求_されます。
- SCP(ガードレール) → 管理グループスコープのAzure Policy — 拒否のみ(deny-only)よりも豊富な効果(監査、拒否、変更、deployIfNotExists)を持ちます。
- IAM role/policy → RBACロール割り当て —(プリンシパル、ロール、スコープ)。IDにアタッチされたポリシーはありません。
- Root-account superpowers → _3つに分割_されます。Global Admin(ID)+ elevateAccess(リソース)+ 請求オーナー(費用)。これら3つすべてを最初から持つ単一のプリンシパルはありません。
- Organizations
CreateAccount→ サブスクリプションエイリアスAPI + 請求スコープ — そして、それが許可されている請求契約タイプの場合のみです。 - IRSA (IAM Roles for Service Accounts) → workload identity federation — 同じOIDCの考え方です。
- Tag-based grouping → リソースグループ — 構造的で必須であり、タグクエリではありません。
- ARN in logs → リソースID(
_ResourceId列)— スコープのコンテキストは構造化されており、名前にエンコードされていません。
Terraformステートのブートストラップ、その変換
これは、AWSのランブックがほぼ機能するが、最初のステップで破綻する場所です。
ご存知のAWSのブートストラップは次のとおりです。管理アカウントでステートバックエンドをコールドスタートし(ローカルステート、その後バックエンド自体に移行)、OUごとのバックエンドのステートをそのルートバックエンドに保持し、独自のバックエンドを持つメンバーアカウントを作成します。これをクリーンにする不変条件は、ルートアカウントは常に存在し、S3バケットを保持できるため、常にルートアカウントからブートストラップできるという点です。
Azureは、その不変条件を最初から破ります。常に存在するオブジェクトはテナントですが、テナントはリソースを保持できません。ストレージアカウントはサブスクリプション内に存在し、サブスクリプションは請求プレーンから生まれます。したがって、Azureの「ルートアカウント」はルートとして指定する任意のサブスクリプションであり、意識的に1つを指定する必要があります。実際には、ほとんどのテナントはサインアップ時に1つのサブスクリプションを取得するため、その並行性はほとんど保たれます。AWSが管理アカウントを提供するのに対し、Azureは最初のサブスクリプションを提供します。
サブスクリプションが既に存在する場合(一般的なケース)の変換されたフローは次のとおりです。
- 一度限りのコールドスタート: 指定されたルートサブスクリプションにローカルステートでステートバックエンドを展開し、次に
init -migrate-stateをそれ自体に実行します。 - サブスクリプションごとのバックエンド、ステートはルートバックエンドに保存: 追加のサブスクリプションごとのバックエンドコンポーネントは、自身のステートをルートバックエンドに固定するため、それらを立ち上げるのは通常の適用です。これ以上コールドスタートは必要ありません。
- 各サブスクリプションのその他すべては、そのサブスクリプション自身のバックエンドを使用します。
ゼロから(コードでサブスクリプションを作成する)の場合、順序は強制されます。まずルートサブスクリプション、次にルートバックエンドです。なぜなら、バックエンドはストレージアカウントであり、ストレージアカウントは存在するためにサブスクリプションを必要とするからです。1つの卵と鶏のジレンマに注意してください。azurermプロバイダー自体がサブスクリプションコンテキストを要求するため、サブスクリプションがゼロの場合、その最初のサブスクリプションを命令型の呼び出し(CLIまたはazapiプロバイダー)で作成し、その後Terraformにインポートする必要があります。
そして、ここにAzureがAWSの経験則よりも本質的にシンプルな点があります。ルートにあるバックエンドとメンバーにあるリソースという構成では、AWSのハブアンドスポークの信頼メカニズムは一切必要ありません。バックエンドのrole_arn、プロバイダーのassume_role、両側信頼ポリシーも不要です。テナントルート管理グループのOwner権限はすべてのサブスクリプションに継承されるため、1つのトークンがテナント全体で機能します。プロバイダーはsubscription_idを設定するだけでサブスクリプション間を「ホップ」し、バックエンドはStorage Blob Data Contributor権限によって承認されたデータプレーンのブロブアクセスにすぎません。クロスサブスクリプションは信頼の交渉ではなく、単なるパラメーターです。(後でカスタムロール、PIM、専用のCI IDを使ってこれを強化しますが、その場合でもスコープでのロール割り当てであり、信頼ポリシーのハンドシェイクではありません。)どちらかの側面を深くコード化したい場合は、HashiCorp Terraform Authoring & Operations Pro試験が、まさにこれらのステートとプロバイダーのパターンを中心に構築されています。
AWSの勘が積極的に誤解を招く4つのポイント
他のすべてを忘れても、これだけは覚えておいてください。
- 「管理者」はグローバルではありません。 Global AdministratorはIDのみであり、誰かがリソースロールを付与するまでサブスクリプションを読み取ることができません。単一の「ルート」プリンシパルは存在しません。
- 権限はIDではなくスコープにアタッチされます。 ユーザー上のポリシーを探すのではなく、管理グループ、サブスクリプション、リソースグループ、またはリソース上のロール割り当てを探してください。
- 請求はリソースとは別の世界です。 「何でもデプロイできる」という事実は、「サブスクリプションを作成できる」という事実は何も教えてくれません。プレーンが異なり、ロールが異なり、
az role assignment listには表示されません。 - リソースグループは構造上重要です。 これはタグではなく、RBACスコープ、ポリシー適用範囲、そして削除境界です。意図的にリソースグループのレイアウトを設計してください。
各側面を掘り下げるための認定資格
クラウドガバナンス — 階層、ID、ガードレール、ステート — は、両クラウドのアーキテクチャおよび管理試験のバックボーンであり、これらの試験の学習は、上記の概念を定着させる最も速い方法でもあります。既にAWS資格をお持ちの場合、同じ行のAzure資格が自然な次のステップです。
AWS側(から知識を変換する):
- AWS Certified Cloud Practitioner (CLF-C02) — アカウント、IAM、およびOrganizationsの基礎。
- AWS Certified Solutions Architect – Associate (SAA-C03) — IAM、マルチアカウント構造、およびコアアーキテクチャ。
- AWS Certified Solutions Architect – Professional (SAP-C02) — マルチアカウントのOrganizations、SCP、および大規模なクロスアカウントアクセス。
- AWS Certified Security – Specialty (SCS-C03) — IAMの詳細、SCP、およびキー管理。
Azure側(知識をに変換する):
- Microsoft Certified: Azure Fundamentals (AZ-900) — テナント、サブスクリプション、リソースグループ、およびRBACの基本。
- Microsoft Certified: Azure Administrator Associate (AZ-104) — 日常的なプレーン:RBAC、リソースグループ、サブスクリプション、およびEntraの基本。
- Microsoft Certified: Azure Solutions Architect Expert (AZ-305) — 管理グループ、ガバナンス、およびランディングゾーンの設計。
- Microsoft Certified: Azure Security Engineer Associate (AZ-500) — Entra ID、RBAC、Azure Policy、Conditional Access、およびKey Vault。
現在AWS認定資格をお持ちの場合の実用的なパスとしては、まずAZ-900から始めて用語をマッピングし、次にAZ-104(最もよく使うリソースプレーンに特化)に飛び、業務がアーキテクチャ寄りかセキュリティ寄りかに応じてAZ-305またはAZ-500を追加することです。
結論
AzureはAWSよりも難しいわけではありません。異なる方法でファクタリングされているのです。AWSはID、リソース、請求権限を1つのプレーンに集約し、すべてを実行できる管理アカウントを提供します。Azureはこれら3つの懸念事項を意図的に分離しており、初日は摩擦のように感じられ、3ヶ月目には明確な境界線のように感じられます。概念を一度変換してください。つまり、1つのプレーンが3つになり、アカウントは別の場所で請求されるサブスクリプションになり、ポリシーはIDではなくスコープにアタッチされ、リソースグループは実際の構造になります。残りは用語の問題です。上記の認定資格でこれを習得すれば、恣意的な拒否のように感じられた部分が、一貫性のある意図的な設計として理解できるようになります。