【初心者向け】AWS CloudFormation を使用してAWS Config required-tags ルールを設定するときに初心者がハマったちょっとした落とし穴

記事タイトルとURLをコピーする

カスタマーサクセス部5課でOJT中の濱田です。
引越ししてからは畳のある部屋を仕事部屋にしたのですが、やはり素足でウロウロするのが気持ちいいです。

さて、本題です。

AWS Organizationsで複数アカウントを一元管理するとともに、それぞれのアカウントにAWS Configを有効化し、Config ルールを一括で設定するという検証の機会がありました。

AWS CloudFormation StackSetsを使用してルールを設定したのですが、設定ファイルを書く際に、いくつか落とし穴にハマって抜け出すのに時間がかかってしまいました。

CloudFormationテンプレートのトラブルシュートって大変ですよね!

CloudFormationのLinterなどを使用することでミスを減らすことはできますが、それでもまだまだ論理的な誤りや細かい部分についてのミスはありうるでしょう。

この記事ではかなりニッチなところを攻めますが、同じように作業に詰まっている方がどこかにいるのでは、と思い、これらの落とし穴について書いておきます。

そもそも、なぜStackSetsを使用するのか

AWS Organizations 配下のマルチアカウントにConfigルールを敷く方法としては、AWS CLIを用いる方法もあります。

docs.aws.amazon.com

この方法もシンプルで魅力的ですが、StackSetsで展開することにも魅力があります。

第一に、新しいアカウントを組織に入れた時、自動的にStackが走るように設定できます。 これは、運用負担を減らすことや、ルールの設定忘れの防止に繋がります。

また第二に、StackSetsを編集するという形で、インターフェースにおいて対話的にConfigルールを設定することが可能です。 StackSetsを変更する形でルールを更新・設定すると、ユーザはCloudFormationのGUIでパラメータを操作することができます。 これは、CLIを使うよりは直感的な方法でしょう。

StackSetsでConfigルール required-tags を設定するときに詰まったこと

今回、組織の全アカウントに特定のタグが付いているかを検証する必要があったため、全アカウントに、Configのマネージドルールであるrequired-tagsを設定する必要がありました。

required-tagsの設定の詳細については、こちらのブログが明快に解説しています。 blog.serverworks.co.jp required-tagsのお陰で、リソースに対する部署ごとのタグ付けを徹底して、このタグを使ってコスト配分を計算したい、といったユースケースに対応しやすくなります。

さて、CloudFormation でこれを実施するためにはYamlもしくはJsonファイルで設定を書き込まなければなりません(濱田は以下Yamlで設定ファイルを紹介します)。

このYamlファイルの設定や、エラーの対応に関して、わたしが詰まった箇所を以下で書いていきます。

基本的な書き方

required-tagsの設定を行う場合、最低限、次のような設定ファイルを書くことになるかと思います。以下ではYamlで例を示します。

#パラメータ設定
Parameters:
  ComplianceResourceTypes:
    Description: "Select the resource type to be monitored by the Config rule"
    Type: List<String>
    Default: "AWS::EC2::Instance"
    AllowedValues:
      - "AWS::EC2::Instance"
      - "AWS::EC2::Volume"
      - "AWS::ElasticLoadBalancing::LoadBalancer"
      - "AWS::RDS::DBInstance"
      - "AWS::S3::Bucket"
  Inputtag1Key:
    Description: "First tag key"
    Type: String
    Default: "Environment"
  Inputtag2Key:
    Description: "Second tag key"
    Type: String
    Default: "Project"
    
#作成するリソースの定義
Resources:
  RequiredTagsConfigRule:
    Type: "AWS::Config::ConfigRule"
    Properties:
      ConfigRuleName: "required-tags"
      Description: "Ensure resources have the required tags."
      Scope:
        ComplianceResourceTypes: !Ref ComplianceResourceTypes
      Source:
        Owner: "AWS"
        SourceIdentifier: "REQUIRED_TAGS"
      InputParameters:
        tag1Key: !Ref Inputtag1Key
        tag2Key: !Ref Inputtag2Key

前半では、スタックを走らせるときに、ユーザに入力してもらうパラメータを用意しています。 この前半部のおかげで、実際にスタックを流す際、次のような入力画面を提示することができます。

Yamlファイルの内容が反映されたもの

複数の入力パラメータを呼び出す時の参照の書き方に注意

さて、注意点です。

上の設定では、ルール準拠の監視対象とするリソースタイプについては、複数選択可能なリスト式にして、AllowedValuesを設定しています。

required-tagsは実際には30のリソースを監視可能ですが、ここでは簡単のために選択肢を5つに絞っています (監視可能なリソースについては以下を御覧ください)。 docs.aws.amazon.com

リソースを手打ちするのはもちろん大変ですので、複数選択のリスト形式で入力を受け付けています。

しかしそれを呼び出す仕方には少し注意が必要です。

わたしははじめ、次のように記述を行ってしまいました。どこが間違っているか、お分かりでしょうか?

#パラメータ設定
Parameters:
  ComplianceResourceTypes:
    Description: "Select the resource type to be monitored by the Config rule"
    Type: List<String>
    Default: "AWS::EC2::Instance"
    AllowedValues:
      - "AWS::EC2::Instance"
      - "AWS::EC2::Volume"
      - "AWS::ElasticLoadBalancing::LoadBalancer"
      - "AWS::RDS::DBInstance"
      - "AWS::S3::Bucket"

#……中略……
     
#作成するリソースの定義
Resources:
  RequiredTagsConfigRule:
    Type: "AWS::Config::ConfigRule"
    Properties:
      ConfigRuleName: "required-tags"
      Scope:
        ComplianceResourceTypes:
      -  !Ref ComplianceResourceTypes

間違っているのは下の三行のうち、ここです。

  Scope:
        ComplianceResourceTypes:
      -  !Ref ComplianceResourceTypes #←ここ!

正しくは、こうです。

  Scope:
        ComplianceResourceTypes: !Ref ComplianceResourceTypes

このハイフンがあるのとないのとは大違いです。

次の画像に示すようなエラーが登場してしまいます。

エラー画面

エラーメッセージは次のようなものになっています。

Properties validation failed for resource RequiredTagsConfigRule with message: [#/Scope/ComplianceResourceTypes/0: expected type: String, found: JSONArray]

文字列(String)が予期されているのにリスト(JSON Array)が入っている! ということが示されていますね。

リストを渡したいのにStringが期待されているとはこれいかに? と一瞬思いますが、ここはハイフンで展開されているリストの内部ですから、この中にさらに配列が入ることが許容されていないのでしょう。

AWSの公式ドキュメントにも、ComplianceResourceTypesの入力はArray of Stringであることが書かれています。「文字列の配列」を許容するわけだから、配列の中には「文字列」が要求されているわけですね!

docs.aws.amazon.com

要するに、!Refはそのまま文字列の配列を手渡すことができるので、リストの形にして参照しなくても良い! ということです。

タグの入力パラメータを用意するも、コンソール上とは異なり、空の入力を受け付けてくれない

次も入力系のエラーです。 required-tagsルールが6つまでタグキーとタグ値を監視対象としてくれることは、ドキュメントにもある通りです。

docs.aws.amazon.com

コンソール上でも、6つのタグキー・タグ値の入力を求められ、1つ目のタグキーは必須項目となっています。

タグキーの入力コンソール。1つ目のタグキーを入力しないとエラーになる

しかし! テンプレートを作成するときには、コンソールのようにはいきません。例えば次のようにテンプレートを作って実行しようとすると……

Resources:

  RequiredTagsConfigRule:
    Type: "AWS::Config::ConfigRule"
    Properties:
      ConfigRuleName: "required-tags"
      Scope:
        ComplianceResourceTypes: !Ref ComplianceResourceTypes
      Source:
        Owner: "AWS"
        SourceIdentifier: "REQUIRED_TAGS"
      InputParameters:
        tag1Key: Environment
        tag1Value:
        tag2Key:
        tag2Value:
        tag3Key:
        tag3Value:
        tag4Key:
        tag4Value:
        tag5Key:
        tag5Value:
        tag6Key:
        tag6Value:

流す以前のところでエラーで止めてくれます。

下部にエラーが出ていますね

エラーメッセージはこんな感じ。

[/Resources/RequiredTagsConfigRule/Type/InputParameters/tag1Value] 'null' values are not allowed in templates

どうやら、CloudFormationテンプレート上では、タグキーおよびタグルールについて、必須かオプションかに関わらず、値を入れずに実行することはできないようです。

ですから、以上のような構築をしたい場合には、結局

Resources:
  RequiredTagsConfigRule:
    Type: "AWS::Config::ConfigRule"
    Properties:
      ConfigRuleName: "required-tags"
      Scope:
        ComplianceResourceTypes: !Ref ComplianceResourceTypes
      Source:
        Owner: "AWS"
        SourceIdentifier: "REQUIRED_TAGS"
      InputParameters:
        tag1Key: Environment

として、不要な行を削除する必要があるようです。

初心者としては、コンソール上のデザインをテンプレートに落とし込めばいいわけではないのだなあ、という学びになりました!

おわりに

ハアハア……、CloudFormationとの戦いはなかなか苦しいものです。

エラーは一個ずつしか見られないし、「今度はうまくいくか⁉️」というトライ&エラーにも、やや時間がかかります。ここがしんどい。

だからこそ、だれかがエラーメッセージなどで検索して、この記事に突き当たること、そして誰かの参考になることを祈っています。

ではまた!

濱田 明日郎(執筆記事の一覧)

人事付

2024年新卒。奄美大島出身。
ベルクソン哲学研究で博士(人間・環境学)。
ITは未経験ですが、優勝目指して頑張ります。