🐡

Azure で IaC (ARM Template/Bicep) するなら PSRule for Azure!#1 [導入編]

2022/09/07に公開

皆様こんにちは。私です。

最近、PSRule for Azure という名の「Azure 向け IaC ツール」を知って、試しに使ってみたところ、なかなかイケてそうな印象を受けました。ということで PSRule を流布すべく筆をとった訳なんですが、文章ボリュームがそこそこ多くなってしまった 書くのに疲れた ので、何回かに記事を分けて紹介しようと思います。

今回は、PSRule for Azure の紹介と、ローカル環境で使う方法を紹介します。

PSRule for Azure とは何か

そもそもの話です。PSRule for Azure は、Azure インフラの定義ファイル (Azure Bicep / Azure ARM Template) と Azure リソースに対する制約 (これをルールと呼びます) を入力にして、各リソースのルール準拠状況を出力する PowerShell ツールです[1]

https://azure.github.io/PSRule.Rules.Azure/

例えば、Azure 仮想ネットワークのサブネットには、ファイアウォール装置である Network Security Group (NSG) を関連付けることが推奨されています。怪しいトラフィック (e.g. C2 へのアクセス、ラテラルムーブメントの通信) を防ぐ為ですね[2]。もし NSG の関連付けをしてないサブネットをうっかり Bicep で定義してしまったらどうなるでしょうか。非推奨構成のままデプロイしてしまって、最終的に重大なセキュリティインシデントに繋がる可能性もありますよね。

安心してください。PSRule for Azure を使えば、ローカルマシンで IaC コードを書いている時に、そのミスに気づくことができます。また、CI (/CD) パイプラインに組み込むことで、マージリクエスト時やデプロイ前に検証するような体制も作れます。PSRule for Azure は、Azure の IaC でガバナンスを担保するためのテスト自動化支援ツールです

なお、PSRule for Azure は、実際は PSRule モジュールと PSRule.Rules.Azure モジュールの二つからなるソフトウェアです。

  • PSRule: PowerShell で IaC コードを検証したり、ルールを定義したりするフレームワークを提供するモジュール。いわばコア機能。
  • PSRule.Rules.Azure: Azure リソースに関して推奨されるルール (詳細は後述) を事前に定義したものをまとめたモジュール。狭義の PSRule for Azure はこれを指す。

なぜ PSRule for Azure なのか

さて PSRule for Azure の役割がわかったところで、次に利点を大きく 4 つ紹介していきます。いずれも個人的な見解に基づくもので、異論を認めます。

  • プリセットルールが充実している
  • CI パイプラインに導入しやすい
  • VS Code 拡張がある
  • 既存リソースも検証できる

利点: プリセットルールが充実している

PSRule for Azure の素晴らしい点のひとつは、公式の推奨事項やベストプラクティスに基づくプリセット ルールが、予め用意されていることです。

先述した NSG の推奨事項のようなものって、実は他にも大量にあるんですよね。Microsoft Azure では、Well-Archtected Framework (WAF) と呼ばれるフレームワークの形で、公式から推奨事項やベストプラクティスを提供しています。ドキュメントを見てもらうとわかりますが、項目が相当あるので 普通の人間 自分の手には負えません。

https://docs.microsoft.com/ja-jp/azure/architecture/framework/

PSRule for Azure は、こうした WAF の推奨事項/ベストプラクティスに基づくプリセットルールを内包しているため、「WAF に準拠したリソース定義になっているか」簡単に検証出来ます。[3]。プリセットルールの一覧は以下にまとめられているので、必要に応じて参照するとよいです。

https://azure.github.io/PSRule.Rules.Azure/en/rules/

また、各プリセットルールに詳細なガイダンスもついているのも、嬉しみポイントです。特に、非準拠なコードの修正方法を示した Suggest changes が非常に有益で、これのおかげで「ルールに準拠してないそ💢」と怒られた時も、心にゆとりを保ったまま修正作業に挑むことができます。

ちなみに、プリセットルールは強力ではあるものの、使わない選択肢も選べるので安心してください。特定のプリセットルールだけを検査対象から除外したり、自作のルールを定義/追加する機能も備えているので、自由にカスタマイズできます。

利点: CI パイプラインに導入しやすい

GitHub Actions と Azure DevOps Pipelines 向けに公式ワークフローが提供されているので、既存の CI パイプラインにも PSRule for Azure を簡単に組み込めます。

例えば GitHub Actions の場合だと、最短 2 行で PSRule for Azure による検証をジョブに導入できます。

- name: Run PSRule analysis
  uses: microsoft/ps-rule@main

利点: VS Code 拡張がある

これは VS Code / Codespaces ユーザーだけが恩恵を受けられるメリットではあるものの、PSRule の VS Code 拡張があるのもユーザーエクスペリエンスの観点で大きなメリットです。Bicep コードを書いてる時、ボタン 1 つで PSRule for Azure の検証を実行できるので、難無く普段の作業に組み込めます。

その他にも、PSRule の VS Code 拡張は、次のような機能を提供しています。

  • PSRule の config ファイル (ps-rule.yaml) の入力補完
  • ルール定義ファイルの入力補完
  • ルール定義ファイルのスニペット

https://marketplace.visualstudio.com/items?itemName=bewhite.psrule-vscode

利点: 既存リソースも検証できる

IaC のツールという体裁で PSRule for Azure を紹介してきましたが、実は Azure 上に存在するリソースに対しても検証ができます。

長いことリソースを管理し続けてると、例外的に IaC 以外で管理するリソースとかどうしても出てくることもあろうかと思いますが、そういう状況で非常に役立つ機能です。

ただ、PowerShell 環境が必須だったり、Az モジュールのインストールやデータのエクスポートなど、少し準備手順は増えます。詳細な手順は追って解説します。

PSRule for Azure の注意点

便利な反面、以下のような注意点もあります。導入に際して少し注意してください。

  • PowerShell 以外で動作しない
    • [補足]: ローカルの検証環境が要らないのであれば、GitHub Actions / Azure Pipelines を使えばいいので PowerShell 環境は必要ない
  • Azure Bicep / Azure ARM Template 用のツールなので、Terraform に対応していない
  • プリセットルールが日本語で定義されてない (2022/09/07 現在)
    • [補足]: 英語が苦手な方は DeepL で何とか…
  • その他 FAQ はこちらに https://azure.github.io/PSRule.Rules.Azure/faq/

ローカル環境を構築する

以下では、VS Code を使って Bicep コードを開発するシナリオを想定して、PSRule の導入方法を紹介します。ちなみに、必要な情報はすべて公式ドキュメントに書いてあります。

https://azure.github.io/PSRule.Rules.Azure/install-instructions/#installing-locally

なお、環境を揃えるのが結構手間なので、Docker で環境構築してしまうのが楽だと思います。自分が作成した devcontainer 環境はこれです。

https://github.com/OpenJNY/psrule-demo/tree/devcontainer

PSRule の準備

PSRule は PowerShell モジュールであり、ローカルで動かすにはいろんなものが必要です。ざっくり言うと、以下のものが必要です。最新の情報は公式ドキュメントのガイドを確認してください。

  • PowerShell Core v7.2 以降
  • .NET SDK v6 以降
  • PowerShell Module
    • PSRule
    • PSRule.Rules.Azure
    • Az[4]
  • Bicep CLI (bicep を扱う場合のみ)

VS Code の準備

VS Code には以下の拡張機能を入れておきます。

.vscode/extensions.json
{
    "recommendations": [
        "bewhite.psrule-vscode",
        "ms-azure-devops.azure-pipelines",
        "ms-azuretools.vscode-bicep",
        "ms-vscode.powershell",
        "msazurermtools.azurerm-vscode-tools",
    ]
}

また、タスクランナーに次のタスクを定義しておきます。

.vscode/tasks.json
{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "PSRule",
            "problemMatcher": [
                "$PSRule"
            ],
            "label": "PSRule: Run analysis",
            "presentation": {
                "focus": false,
                "panel": "dedicated",
                "clear": true
            }
        }
    ]
}

ディレクトリ構造

今回採用するディレクトリ構造は次の通りです。ポイントは、ps-rule.yaml をルートディレクトリに配置することと、Bicep の配置場所 (azure/) を決めておくことです。

.
├── .devcontainer/
├── .ps-rule/ <------------------------- 独自ルールを定義するディレクトリ
├── .vscode/
├── azure/
│   ├── deployment/
│   │   ├── project01/
│   │   │   └── main.bicep <------------ project01 の Bicep ファイル
│   │   └── project02/
│   │       └── main.bicep <------------ project02 の Bicep ファイル
│   └── modules/
│       └── my-module/
│           ├── main.bicep <------------ Bicep Module の定義ファイル
│           └── .tests/
│               └── main.tests.bicep <-- Bicep Module のテスト
└── ps-rule.yaml <---------------------- PSRule の設定

ローカル環境で遊んでみる

まずは用意したローカルの環境でいろいろ遊んでみることにします。

PSRule の設定

quickstart の ps-rule.yaml を参考に、config ファイルをルートディレクトリに配置します。

ps-rule.yaml
ps-rule.yaml
#
# PSRule for Azure configuration
#

# Please see the documentation for all configuration options:
# https://aka.ms/ps-rule/options
# https://aka.ms/ps-rule-azure/options

# Configure binding for local rules.
binding:
  preferTargetInfo: true
  targetType:
    - type
    - resourceType

# Require minimum versions of modules.
requires:
  PSRule: "@pre >=2.2.0"
  PSRule.Rules.Azure: "@pre >=1.17.1"

# Use PSRule for Azure.
include:
  module:
    - PSRule.Rules.Azure

output:
  culture:
    - "ja-JP"

input:
  pathIgnore:
    # Ignore other files in the repository.
    - ".devcontainer/"
    - ".vscode/"
    - ".github/"
    - "*.md"

    # Exclude modules but not tests.
    - "azure/modules/**/*.bicep"
    - "!azure/modules/**/*.tests.bicep"

configuration:
  # Enable automatic expansion of Azure parameter files.
  AZURE_PARAMETER_FILE_EXPANSION: true

  # Enable automatic expansion of Azure Bicep source files.
  AZURE_BICEP_FILE_EXPANSION: true

  # Configures the number of seconds to wait for build Bicep files.
  AZURE_BICEP_FILE_EXPANSION_TIMEOUT: 10

いくつかの重要な設定項目について補足します。詳細はドキュメントを確認してみてください。

  • include: は、対象のルールを指定します。既定では、.ps-rule/ 以下に配置されたカスタム ルールのファイル (.ps1) のみが対象となっています[5]。今回の設定では、さらに追加して PSRule.Rules.Azure モジュールの全てのプリセットルールを読み込んでいます。[6]
  • input: は、検証対象の bicep ファイルや Azure リソースを指定します。既定では、いくつかの除外ディレクトリやファイル [7] を除き、プロジェクト配下のすべてのファイルが検査対象です。また、すべての種類の Azure リソースが検査対象です。今回の設定では、対象のファイルを限定するため pathIgnore ディレクティブを使っています。

ローカル Bicep ファイルの検証

試しに ./azure/deployment/project01/main.bicep を次の通り作成します。

azure/deployment/project01/main.bicep
@description('storage account name')
param storage_account_name string = 'st${uniqueString(resourceGroup().name)}'

@description('storage account location')
param location string = 'eastus'

resource storageaccount 'Microsoft.Storage/storageAccounts@2021-09-01' = {
  name: storage_account_name
  location: location
  kind: 'StorageV2'
  properties: {
    supportsHttpsTrafficOnly: true
    minimumTlsVersion: 'TLS1_2'
  }
  sku: {
    name: 'Premium_LRS'
  }
}

一見すると、単にストレージアカウントをデプロイするだけの問題のない定義ファイルに見えます。実際、Bicep コマンドを使ってデプロイ出来ますし、構文的には問題ありません。

この定義ファイルを PSRule で検証するとどうなるか試してみます。VS Code のタスクランナーで PSRule: Run analysis を実行します。

Command Palette (Ctrl + P) > Tasks: Run Task > PSRule: Run analysis

なんだかいっぱいエラーが出てきました・・・。2022/09/07 時点で検出できたエラーは 4 つでした。

  • Blob の論理削除を有効化しろ
  • Blob の匿名パブリック読み取りを無効化しろ
  • リソースにタグを付けろ
  • ストレージアカウントがパブリックアクセス可能な状態になってるから、ファイアウォールを設定しろ

なるほど、言われてみるとそうかもしれん。ということで直してみました。

修正後のmain.bicep
resource storageaccount 'Microsoft.Storage/storageAccounts@2021-09-01' = {
  name: storage_account_name
  location: location
  kind: 'StorageV2'
  properties: {
    // Blob の匿名パブリック読み取り無効化
    allowBlobPublicAccess: false
    supportsHttpsTrafficOnly: true
    minimumTlsVersion: 'TLS1_2'
    // ファイアウォール設定
    networkAcls: {
      defaultAction: 'Deny'
    }
  }
  sku: {
    name: 'Premium_LRS'
  }
  // タグ付け
  tags: {
    env: 'dev'
  }
}

resource blobservice 'Microsoft.Storage/storageAccounts/blobServices@2021-09-01' = {
  parent: storageaccount
  name: 'default'
  properties: {
    // 論理削除の設定
    deleteRetentionPolicy: {
      allowPermanentDelete: false
      days: 10
      enabled: true
    }
  }
}

修正後にもういちど Task を実行すると・・・エラーが出なくなりました!これでガバガバ ガバナンスなリソースが世に放たれることを防ぐことができました。めでたしめでたし🙌

既存リソースの検証

PSRule はデプロイされた Azure リソースに対しても、簡単にルール検証できます。

試しに、Azure Portal で新しくリソースグループ rg-test を作成し、Azure Portal のデフォルト設定で (i.e. [Next >] ボタンだけ押して [Create] した) ストレージアカウントをデプロイしておきます。

次に、Azure 上の情報を手元にエクスポートします。Export-AzRuleData を実行するには、Az.AccountsAz.Resources モジュールが必要になるので、もし Azure Powershell を導入していない環境であれば、事前に入れておいてください。

Connect-AzAccount
Select-AzSubscription -SubscriptionId $subid

# cd <project-dir> && mkdir out
Export-AzRuleData -ResourceGroupName 'rg-test' -OutputPath 'out/'

./out/<subid>.json が生成されていればエクスポート成功です。以下のコマンドで PSRule のプリセットルールの準拠状況を確認してみましょう。

Invoke-PSRule -InputPath 'out/' -Module 'PSRule.Rules.Azure' -Outcome Fail

先ほどストレージアカウントを手元で定義した時と同じようなエラーが出ていますね。本番環境のリソースを Azure Portal のデフォルト設定のまま作ることはまず無いと思いますが、もし設定ミスや修正すべき点があったとしても、こうしてツールで一括チェックできるのは非常に便利です。

Invoke-PSRule は、結果を Markdown や Csv で出力することもできます。もしレポートのような形でサマリーを残したければ、-OutputFormat Markdown のコマンドパラメータを追加して、Markdown 文書を生成すると良いでしょう。

まとめ

今回の記事では、PSRule for Azure と呼ばれる IaC ツールを紹介しました。PSRule for Azure と VSCode を使えば、自分が書いてる IaC コードが WAF (Microsoft が提唱するベストプラクティス フレームワーク) に準拠できているか、簡単に検証できることを実際に確認しました。さらに、デプロイ前の IaC コードだけではなく、すでに Azure 上にデプロイされたリソースに対しても、同様の検証ができる事を確認しました。

ローカル環境への導入は少々ややこしいものの、一度環境構築していまえばインフラエンジニアの皆さんの強力な味方になってくれるはずです (devcontainer 使えば環境構築も楽だよ)。

次の記事ではパイプラインに組み込む方法をメインに解説したいと思います。

参考情報

https://azure.github.io/PSRule.Rules.Azure/

https://github.com/Azure/PSRule.Rules.Azure-quickstart

https://techcommunity.microsoft.com/t5/itops-talk-blog/psrule-introduction-to-infrastructure-as-code-iac-testing/ba-p/3580746

脚注
  1. 実際の PowerShell コマンドに渡す入力 (arguments) や出力 (e.g. stdout) の話ではないです。 ↩︎

  2. ネットワーク層 (L4) の ACL だけで防げる攻撃ではないので、しっかり多層防御やゼロトラストしましょう。 ↩︎

  3. PSRule のプリセットルールは完全ではないので、PSRule for Azure があれば WAF は意識しなくていい訳ではないです。プリセットルールに足りないルールがあれば Issues でリクエストしてみましょう https://github.com/Azure/PSRule.Rules.Azure/issues/new/choose ↩︎

  4. Az モジュールは、一部の PSRule 機能を使う場合に限り必要になります。なお、正確には Az.Accounts と Az.Resources があれば十分です。https://azure.github.io/PSRule.Rules.Azure/install-instructions/#installing-locally ↩︎

  5. カスタムルールの置き場の指定には includePath を使う https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Options/#includepath ↩︎

  6. モジュールで提供されるプリセットルールの読み込みは includeModule を使う https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Options/#includemodule ↩︎

  7. 既定で検査対象から外れるディレクトリやファイルたち https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Options/#inputignorerepositorycommon ↩︎

Discussion