Young Leaves

Bicep リンターでBicep のコードをチェックする

IaC でもプログラムのコードと同様に静的コード解析しコードの品質やレビュー工数を削減したいと思うことがあります。今回はBicep CLI に付属するBicep リンターを使い、Bicep のテンプレートファイルをチェックする方法を説明します。

実施環境

Azure CLI

2.62.0

Bicep CLI

0.29.47

静的コード解析とは

静的コード解析はプログラムなどのファイルを実行せずコードの構文エラーやスタイル、脆弱性、変数の命名規則などをチェックすることです。静的コード解析ツールはこれらのコードを解析するツールを指します。静的コード解析ツールを利用することで、社内のコーディング規約を遵守させたりコードの脆弱性を事前にチェックできます。開発時にコードの問題を指摘し修正することでコードの品質向上やレビュー工数の削減に繋げられます。ただし、例外のスローなどファイルの実行を必要とするエラーは検出できないため注意が必要です。

静的コード解析ツールは導入が簡単であり、基本的なコードの記述を検知するにはとても便利です。また、コマンドで実行可能なツールが多く、CI/CD に組み込むことでリポジトリへのプッシュ時やPull Request 時のチェックとしても利用できます。

IaC においてもプログラムと同様に静的コード解析を導入し活用することで、コードの品質やベストプラクティスを遵守した各種リソースの作成に役立ちます。

Bicep リンターとは

Bicep リンターはBicep ファイルの構文エラーやベストプラクティス違反を検出できる機能です。Bicep リンターはBicep CLI の機能の一部のため、Bicep CLI をインストールすることで利用できます。また、Visual Studio Code (以下VSCode) のBicep 拡張機能にも含まれているため、エディタ内でもBicep リンターを利用できます。

Bicep リンターのテストケースはARM テンプレートテストツールキット (arm-ttk) から取得されます。Bicep CLI やVSCode のBicep リンターでは全てのテストケースは実行されず警告の一部のみチェックします。

Bicep リンターはbicepconfig.json ファイルを利用することでリンタールールの検知やパラメーターをカスタマイズできます。Azure リソースに関する詳細な静的コード解析を行いたい場合はサードパーティー製のツールを利用する必要があります。

Bicep リンターでチェックする内容

Bicep リンターでチェックするリンタールールは以下ドキュメントの同配下に記載されています。

リンター ルール - 管理者ユーザー名をリテラルにすることはできない

全てを説明すると膨大な量になるため、リンタールールの一部を抜粋して説明します。

モジュールの場所のパラメーターに明示的な値を使用します

Bicep でモジュールを利用する場合、location の指定を明示的に設定しないことにより想定外の値となる可能性があります。例えば以下モジュールのように resourceGroup().location を利用すると既定の値が設定されます。

param location string = 'japaneast'

// NG:locationを明示的に設定していないため、モジュール内のlocationを参照する
module moduleTest 'module.bicep' = {
  name: 'moduleTest'
}

resource storageaccount 'Microsoft.Storage/storageAccounts@2024-03-01' = {
  name: 'storageaccount'
  location: location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}
// NG:モジュール内の規定が東日本以外の場合、他のリージョンが設定される可能性があるため警告となる
param location string = resourceGroup().location

resource staging 'Microsoft.Storage/storageAccounts@2024-03-01' = {
  name: 'stagingTest'
  location: location
  kind: 'StorageV2'
  sku: {
    name: 'Premium_LRS'
  }
}

上記内容を解消するためには以下のように記述します。

param location string = 'japaneast'

// OK:モジュールに対しlocationを明示的に設定するため差異が発生しなくなる
module moduleTest 'module.bicep' = {
  name: 'moduleTest'
  params: {
   location: location
  }
}

resource storageaccount 'Microsoft.Storage/storageAccounts@2024-03-01' = {
  name: 'storageaccount'
  location: location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}

未使用のパラメーターがない

Bicep テンプレートファイルなどでパラメーターの定義を行っているが一度も利用されていない場合に警告されます。不要なパラメーターをそのまま残すことで、可読性の低下や脆弱性に繋がるため不要なパラメーターは削除します。

// NG:resourceGroupNameを利用していないため警告となる
targetScope = 'subscription'

param resourceGroupName string = 'rg-test'

resource rg 'Microsoft.Resources/resourceGroups@2024-03-01' = {
  name: 'rg-test'
  location: 'japaneast'
}

// OK:resourceGroupNameをnameに利用しているため合格
targetScope = 'subscription'

param resourceGroupName string = 'rg-test'

resource rg 'Microsoft.Resources/resourceGroups@2024-03-01' = {
  name: resourceGroupName
  location: 'japaneast'
}

セキュリティ保護されたパラメーターの規定値

Bicep テンプレートファイルなどで「@secure」を利用したパラメーターに対し、規定値を設定すると警告されます。管理者パスワードなどに規定値を設定した場合、本来閲覧できないユーザーがシークレット値を閲覧可能となります。リンタールールを解決するには何も設定しないか空文字を設定する、newGuid 関数を利用し文字列を生成します。

// NG:セキュリティ保護されたパラメーターに規定値を設定しているため警告となる
@secure()
param databasePassword string = 'p3gT3!f-rGe2BF'

// OK:セキュリティ保護されたパラメーターに規定値を設定しない
@secure()
param databasePassword string

// OK:セキュリティ保護されたパラメーターに空文字を設定する
@secure()
param databasePassword string = ''

// OK:セキュリティ保護されたパラメーターをnewGuid関数で生成する
@secure()
param databasePassword string = newGuid()

Bicep リンターの使い方

Bicep リンターの基本的なコマンドは以下となります。

az bicep lint <オプション>

各オプションの意味は以下の通りです。Bicep リンターでチェックするファイルを指定する「--file / -f」のみ必須オプションとなります。

--file / -f

Bicep リンターでチェックするファイルを指定する。必須オプション。

--diagnostics-format

Bicep リンターの出力形式を指定します。default はテキスト形式、sarif はJSON 形式で出力されます。規定値はdeafult。

--no-restore

外部モジュールを利用せずにパラメーターファイルを作成します。規定値は外部モジュールを利用しパラメーターファイルを作成します。

Bicep リンターを実行し、リンタールールに抵触するものがあると警告として表示されます。

# Bicepリンターでmain.bicepをチェックする
az bicep lint -f main.bicep

C:\Users\IKEAShark\Documents\code\bicep\main.bicep(25,7) : Warning no-unused-params: Parameter "databasePassword" is declared but never used. [https://aka.ms/bicep/linter/no-unused-params]
C:\Users\IKEAShark\Documents\code\bicep\main.bicep(25,7) : Warning secure-secrets-in-params: Parameter 'databasePassword' may represent a secret (according to its name) and must be declared with the '@secure()' attribute. [https://aka.ms/bicep/linter/secure-secrets-in-params]

--diagnostics-format でsarif オプションを指定するとJSON 形式で出力されます。

# Bicepリンターの結果をJSON形式で出力する
az bicep lint -f main.bicep --diagnostics-format sarif

{
  "$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.6.json",
  "version": "2.1.0",
  "runs": [
    {
      "tool": {
        "driver": {
          "name": "bicep"
        }
      },
      "results": [
        {
          "ruleId": "no-unused-params",
          "message": {
            "text": "Parameter \"databasePassword\" is declared but never used. [https://aka.ms/bicep/linter/no-unused-params]"
          },
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "file:///C:/Users/IKEAShark/Documents/code/bicep/temp/main.bicep"
                },
                "region": {
                  "startLine": 25,
                  "charOffset": 7
                }
              }
            }
          ]
        },
        {
          "ruleId": "secure-secrets-in-params",
          "message": {
            "text": "Parameter 'databasePassword' may represent a secret (according to its name) and must be declared with the '@secure()' attribute. [https://aka.ms/bicep/linter/secure-secrets-in-params]"
          },
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "file:///C:/Users/IKEAShark/Documents/code/bicep/temp/main.bicep"
                },
                "region": {
                  "startLine": 25,
                  "charOffset": 7
                }
              }
            }
          ]
        }
      ],
      "columnKind": "utf16CodeUnits"
    }
  ]
}

特定のBicep リンタールールでチェックしたくない、などカスタマイズを行いたい場合はbicepconfig.json ファイルを作成します。例えば「パラメーター内のシークレットをセキュリティで保護する」のルールを抑止する場合はlevel の値をoff にします。

{
  "analyzers": {
    "core": {
      "enabled": true,
      "rules": {
        "secure-secrets-in-params": {
          "level": "warning"
        }
      }
    }
  }
}

bicepconfig.json の詳細な設定方法は以下ドキュメントを参照してください。

Bicep 構成ファイルにリンター設定を追加する

応用的な使い方

Bicep リンターはBicep CLI で実行可能なため、CI やPull Request に組み込めます。Bicep の修正後にBicep リンターを実行させ、基本的な構文エラーやベストプラクティス違反を早い段階で検知し修正を促すこともできます。また、Pull Request 時にBicep リンターを実行させることで、修正箇所のレビュー時の確認箇所を減らすこともできます。

Bicep リンター以外の静的コード解析ツール

Bicep リンター以外にもBicep テンプレートファイルに静的コード解析を実行できるツールを紹介します。3つのツールはコードのスタイルよりAzure リソースのセキュリティやコンプライアンスに関する脆弱性を検知するツールが多めとなります。

  • Checkov
  • KICS
  • SonarQube

Checkov

Checkov は、IaC のセキュリティを強化するためのオープンソースの静的コード解析ツールです。Terraform やCloudFormation、Azure Resource Manager (ARM) 、Kubernetes などのIaC テンプレートを対象に、セキュリティやコンプライアンスの問題を自動的に検出することができます。750を超える組み込みのポリシーだけでなく、独自のカスタムポリシーも構成できます。また、Prisma Cloud と統合することでランタイムスキャンやドリフト検出、プルリクエストの違反に注釈をつけることもできます。

KICS

KICS は、Checkmarx Ltd. が開発したオープンソースの静的コード解析ツールです。主にIaC のセキュリティとコンプライアンスを強化するために使用されます。Terraform やCloudFormation、Azure Resource Manaer (ARM) 、Pulumi、Ansible など幅広いツールに対応しています。また、ローカルPC へのインストールだけでなくDocker コンテナを利用した環境も作成できるため導入も簡単です。

SonarQube

SonarQube はSonarSource が提供している様々なプログラミング言語やフレームワーク、IaC プラットフォームの静的コード解析を行うツールです。WebUI を使ったコードの品質やセキュリティに関するチェックを行えるのが特徴です。また、幅広くサポートしているため静的コード解析ツールを統一できる強みもあります。

まとめ

  • 静的コード解析はファイルを実行せずにコードのスタイル、変数の命名規則、構文エラーをチェックすること
  • Bicep リンターはBicep テンプレートファイルの構文エラーやベストプラクティス違反を検知できる機能
  • Bicep リンターでチェックするには「az bicep lint」コマンドを使う
  • セキュリティ、ガバナンスなど詳細な静的コード解析を行いたい場合はサードパーティー製のツールも検討する

参考資料