ガバナンスが効くAzureの設定をゼロから構築した話

Kevinrobot34
The Finatext Tech Blog
18 min readApr 23, 2024

--

はじめに

こんにちは、Finatextグループのナウキャストでデータエンジニアをしているけびん(X: @Kevinrobot34 )です。会社の様々な開発基盤を作るチームに所属していて、データエンジニアとしてパイプラインを開発しつつ、様々なクラウドを便利に利用できる仕組みを作る(CCoE的な)仕事を担当しています。

ところで、皆さんはLLMを利用していますか?
弊社でも生成AIが関連しているプロダクトを日々利用したり、開発したりしています。
https://nowcast.co.jp/news/20240419/

昨今各種クラウドが生成AIに関するサービスやAPIなどをリリースしていますが、やはり Azure は Azure OpenAI Service を利用できるのでキャッチアップが欠かせなくなっていると考えています。

このような状況を踏まえ、昨年末からガバナンスが効くような形で Azure の環境を整備しました。様々な設定をしたのですが、ここでは特に重要だった以下の6つのポイントにフォーカスし、それぞれどのようなことをしたのか紹介していこうと思います。

  • Billing の階層の整備
  • リソース階層の整備
  • ログをWORMなストレージに集約
  • Azure Policy による予防的・発見的統制の整備
  • 新規サブスクリプションを作成するためのフローの整備
  • Azure OpenAI Service の設定

注意点

弊社では Microsoft Customer Agreement (MCA) でMicrosoft と直接従量課金制で契約をしており、これを前提とします。Enterprise Agreement など他の契約の場合でも当てはまる話がほとんどですが、Billing周りの設定などは違い得るのでご注意ください。

Billing の階層の整備

Azure を利用する際に考えるべきな軸は様々なものがありますが、そのうちの一つに「会計・支払いの管理」があります。支払い方法は何か、請求書は誰・どこの会社宛か、などです。

こういった会計に関する取り扱いをするために、Azure には会計に関する設定の階層があり、

  • Billing Account:契約の単位
  • Billing Profile:請求書の単位、支払い方法の紐付けはここ
  • Invoice Section:請求書内でコストをグループ化する単位

という3つのスコープがあります。

https://learn.microsoft.com/ja-jp/azure/cost-management-billing/manage/view-all-accounts#microsoft-customer-agreement より

Finatextホールディングスには、僕が所属するデータ事業を行う「ナウキャスト」だけでなく、複数の子会社が存在します。それぞれの会社がAzureを利用しており、その会社ごとに支払方法(会社のクレジットカードなど)を設定できるようにしたり、各社の経理担当のメンバーにメールで請求書を連携したりする必要がありました。
そこで以下のように各階層を用意しました。

  • Billing Account : Finatext Group で一つ
  • Billing Prodile : 会社ごとに作成し、対応する支払方法を設定し、経理担当メンバーへのメールでの請求書連携の設定も行う
  • Invoice Section : 会社ごとに一つだけ作成
今回設定した会計の階層のイメージ

支払い方法は会社ごとに設定したいので “Billing Profile” は会社ごとの作成し、各社のクレカを支払い方法として紐づけるようにしています。

また “Invoice Section” については大きな会社で事業部ごとに支払いなどを分割してみられるようにしたいときに複数作ると良いようですが、現状の規模感だと不要と考え、このような構成にしました。

サブスクリプションがどの Invoice Section に属するかは、サブスクリプション作成時に設定します。作成後に後から変更することも可能なようですが、制限もあるようなので、ここで紹介した会計に関する設定は最初に設計をやり切るのが大事になります。

リソース階層の整備

Azure で開発を進めるにあたり、Azureのリソース階層を踏まえ Azure リソースをどのように管理するかも非常に重要です。

https://learn.microsoft.com/ja-jp/azure/cloud-adoption-framework/ready/azure-setup-guide/organize-resources より

ガバナンスが効いた開発を進めるためには適切なガードレールを設定することが大事です。後述する通りAzure Policyを利用することで統制をかけておりますが、一方でアジリティを維持するために開発環境では少し弱めの統制にし本番環境では強めの統制を行う、といった調整をしたくなります。

そこで、Management Group を適切に設定することにしました。弊社では AWS をメインで利用しており Organizational Units を適切に設定することによるマルチテナント環境整備が進んでおりまして、これを参考にすることにしました。具体的には、

  • サービス用環境(Service) or 社内管理用環境(Management)
  • 本番環境(Prod) or 非本番環境(SDLC)

という2つの階層を作り、それぞれに適切なAzure Policyを設定することで、セキュリティと開発効率のバランスが取れるようにしました。

例えば Management-SDLC のグループには sandbox 環境のサブスクリプションを置いており、検証を進めやすいように強すぎない統制をかけています。一方 Service-Prod のグループにはサービスの本番環境サブスクリプションが置いてあり安全性を担保するために強めの統制をかけるようにしています。

Management Group と サブスクリプションの階層のイメージ

Azure と AWS とではプラクティスも違う部分もあるかもしれませんが、今のところ問題なく運用できております。

Management Group を作成し、後述する Azure Policy と組み合わせて使っていく場合、 Management Group の設定・管理とサブスクリプションの配置は非常に重要になります。
しかしデフォルトの設定ではすべてのユーザーが新しい管理グループを作成することができてしまいます。リソース階層を保護し適切なガードレールを運用するために、 “Hierarchy Settings Administrator” などの権限がないと管理グループの設定を変更できないようにする必要があるので注意しましょう。

ログをWORMなストレージに集約

システム操作のトレーサビリティを確保するためにログはとても重要です。ログが満たすべき性質は様々なものがありますが、以下は特に重要ではないでしょうか?

ログが改善されていないことを担保する(完全性の担保)

  • “Write Once, Read Many (WORM)” な状態でログを保存する

ログを集約しておく

  • 集約しておくことで権限管理(機密性の担保)や、WORMの設定の担保などがしやすい
  • また集約されていることで分析もしやすくなる(可用性の担保)

新しくAzureのセットアップをするにあたり、これらの設定をしました。

まず前提としてログは数年以上にわたり長期間保存しておきたいので、 Azure Storage に保存することにしました。何年保存すべきかは要件によって変わりますが、以下の新井さんの投稿が参考になります。

完全性の担保をするためにWORMにログを保存する方法ですが、 Azure Storage で immutable policy を設定すれば実現可能です。

またログの集約は、ログを保存するためのサブスクリプションを作成し、 Azure Storage はこのサブスクリプションに作成するようにしました。
この際にはいくつか注意点があります。

  • ネットワークの制限がかけられている場合には、マイクロソフトのサービスはバイパスすることを許可する設定が必要です
  • Azure Storage Account と対象のリソースは同じリージョンになくてはなりません

これらに注意してログ集約のサブスクリプションと Azure Storage を用意し、各種ログのエクスポート先として指定できるようにしました。以下のように Activity Log や Resource Log を保存していくことができます。

ログの種類によってスキーマやパスの構造は異なるので、ドキュメントを参照しながら分析できる環境は適宜用意する必要があります。弊社ではSnowflakeも利用しているので、これらのデータを外部テーブルなどとして読み込み分析できる環境を作ろうと考えています。

Azure Policy による予防的・発見的統制

クラウドを安全に使うためにはガードレールを適切に設定しておくことが必要不可欠です。ガードレールにも種類があり、主要なものとして「予防的統制」と「発見的統制」があります。

予防的統制

  • 想定される危険なイベント・行動・問題を事前に定義しておき、その発生をできる限り予防するもの
  • ネットワークへの不正アクセスや、意図しないシステムの変更などを防ぐ、第一の防御手段
  • AWS だと Organization SCPs などが該当

発見的統制

  • セキュリティに関わるイベントが発生した際に、問題の早期検知と記録を行い、進行中のインシデントに対して関係者にアラートをあげるような統制
  • システムに影響を及ぼす脅威やリスクの全体的な視認性を高めるのに使用する
  • AWS だと Config などが該当

予防的統制と発見的統制は両方とも同時に行っておくべきものにあります。Azure では Azure Policy を使うことでこれが実現できます。 Azure Policy は様々な効果がありますが、 Deny を使うことで予防的統制を、 AuditAuditIfNotExists を使うことで発見的統制を行うことができます。

RBACのロールなどと同じく、Azure Policy でも大量な builtin policy が用意されており途方に暮れるほどでした。そこでまず CIS Benchmark を読むことにしました。CIS Benchmark には Azure のサービスごとに様々なベストプラクティスがまとまっています。その内容としてはプラクティスの概要から、それを builtin のAzure Policy で実現する方法まで記載されており、ガードレールを検討する際にまず読むのをオススメします。

Azure のドキュメントにはサービスごとに builtin の Azure Policy がまとまっていたりするので、この辺りも一通り読みました。具体的に設定した Policy は

  • 指定したサービスしか利用できないように予防的統制
  • 指定したサービスは japaneast でしか使えないように予防的統制
  • Azure Storage や Azure OpenAI Service へのアクセスで HTTPS を利用させる予防的統制
  • Azure Storage や Azure OpenAI Service で resoure log の出力されることを監視する発見的統制

などがあります。一部は builtin policy では実現できなかったため custom policy を作成しました。しかし大体似たような builtin policy が用意されていることが多いので、まずは builtin policy をひたすら見てそれらを使えないか模索することが多かったです。

これにより Azure Portal の Policy の画面から以下のように統制の状況確認が簡単にできるようになりました。

https://learn.microsoft.com/ja-jp/azure/governance/policy/assign-policy-portal より

Portal から統制の状況の確認がしやすくなりましたが、特に発見的統制は見つかったら早めに確認したいので、今後はPolicyによる監査の結果をSlackに通知したりする仕組みを作れたりすると良いなと考えています。

また Azure Policy には予防的・発見的統制以外にも、様々な機能があり、発見した問題に対して修復まで行うといったことも可能なので、これらについても検討していきたいと思っています。Terraform でのリソース管理との両立のバランスが難しいなと思っていますが、より安全で便利な環境を目指すために Azure Policy を引き続き活用していく予定です。

サブスクリプション作成フローの整備

Azure のサブスクリプションはプロジェクトやプロダクトごとに、また開発の環境(dev/prodなど)ごとに切り分けようと考えてサブスクリプションを複数作成していたのですが、 MCA では最初サブスクリプションを5個しか作れないという制限があるのをご存知でしょうか?

・Azure.com を通じて直接購入した Microsoft 顧客契約には、最大 5 つのサブスクリプションを含めることができます。
・24 時間ごとに 1 つのサブスクリプションを作成することができます。https://learn.microsoft.com/ja-jp/azure/cost-management-billing/troubleshoot-subscription/create-subscriptions-deploy-resources より

MCAで本格利用する前にはまずこれをサポート経由で上限を上げてもらう必要があります。意外と時間がかかるので注意が必要です。

次に本格的にAzureを利用するにあたり、サブスクリプションの作成フローを整理しました。いろいろ整理した結果、大きく分けて2つのステップにまとまりました。1つ目は実際にサブスクリプションを作成するステップで、2つ目はTerraformで初期のリソースを作成するステップです。それぞれ意識したポイントを紹介します。

まず一つ目の実際にサブスクリプションを作成するステップですが、ここでも権限管理をしっかり行いました。サブスクリプション作成はガバナンスのために一部のメンバーしかできないようにしたいですが、一方でCTOしかできないといった状況は避けたいです。
これは Azure RBACのロールではなく Billing の階層のリソースの IAM の機能を使うことで実現ができました。具体的には Invoice Section の IAM に “Azure subscription creator” というロールがあり、これを必要なメンバーにのみ割り当てるようにすれば良いです。

次にTerraformで初期のリソースを作成するステップでの工夫ですが、工数を削減するために適宜モジュールを作成しました。前の節で紹介したように様々なログは一つのサブスクリプションに集約したいので、そのための診断設定などを簡単に作成できるようにしています。また IAM のロールの割り当ての記述も簡単に行えるような工夫も行なっています。

これらの工夫により、数時間もあれば初期設定も含め新しいサブスクリプションを用意することができるようになりました。

Azure OpenAI Service の設定

新しくサブスクリプションを作成しても、そのままでは Azure OpenAI Service を利用することはできません。よく知られているようにマイクロソフトへの利用申請とオプトアウトの申請が必要です。オプトアウト申請については以下の Japan Cognitive Searvices Support Blog が詳しいです。

自分が10個ほどの新規サブスクリプションを作成した際には、それぞれ

  • Azure OpenAI Service の利用申請:約1週間ほど
  • Azure OpenAI Service のオプトアウト申請:約1週間ほど

の追加の時間が必要でした。合計で10日から2週間ほどの追加の時間が必要なイメージです。会社のメンバーにはこれらを踏まえ、新しいサブスクリプションが必要であれば早めに連絡できるように依頼しています。

オプトアウト申請が問題なくできているかを確認するために以下のような Azure Policy を作成し簡単に状況確認ができるようにもしております。便利なのでおすすめです。

{
"properties": {
"displayName": "policy_ai_service_content_logging",
"policyType": "Custom",
"mode": "Indexed",
"version": "1.0.0",
"parameters": {
"effect": {
"type": "string",
"metadata": {
"description": "The effect determines what happens when the policy rule is evaluated to match",
"displayName": "Effect"
},
"allowedValues": [
"Audit",
"Deny"
]
}
},
"policyRule": {
"if": {
"allOf": [
{
"equals": "Microsoft.CognitiveServices/accounts",
"field": "type"
},
{
"equals": "OpenAI",
"field": "kind"
},
{
"count": {
"field": "Microsoft.CognitiveServices/accounts/capabilities[*]",
"where": {
"allOf": [
{
"equals": "ContentLogging",
"field": "Microsoft.CognitiveServices/accounts/capabilities[*].name"
},
{
"equals": "false",
"field": "Microsoft.CognitiveServices/accounts/capabilities[*].value"
}
]
}
},
"notEquals": 1
}
]
},
"then": {
"effect": "[parameters('effect')]"
}
},
"versions": [
"1.0.0"
]
},
...
}

まとめ

これら6つの設定は一つ一つはよく知られているような話かもしれないですが、これらをすべて設定することでガバナンスが効いたAzure環境を作り上げることができたかなと思っています。
しかし、例えば Microsoft Entra ID にまつわる様々な設定など、まだまだ必要なことも残っており、引き続き検証や設定の作業を進めていく予定です。

仲間を募集中です!

Finatextホールディングスでは一緒に働く仲間を募集中です!様々なエンジニア系のポジションがあるので気軽に覗いてみてください!

気になることがあれば気軽にXで @Kevinrobot34 にご連絡ください!

--

--

Data Scientist, Data Engineer at Nowcast Inc. / Kaggle: Master / AtCoder: Blue