【小ネタ】AWS BackupのCloudformationテンプレートではConditionsプロパティでタグの絞り込み条件を設定したほうがよい

# 要点

「AWS Backup」というバックアップのためのサービスがあり、私の担当業務ではEC2インスタンスのバックアップ等でありがたく利用させて頂いています。
AWS Backupの関連リソース構築作業の手間を簡略化するためにCloudFormationテンプレートを作成しようと"AWS Backup CloudFormation"などで検索すると、"ListOfTags"でバックアップ対象のタグを指定する例が多く出てきますが、タグで対象リソースを指定できるプロパティは **"Conditions"と"ListOfTags"の２種類** あります。  

もし複数タグを指定した「バックアップ対象リソースの絞り込み」を行いたい場合、"ListOfTags"プロパティを使用すると期待に反する結果になる可能性があり、たいていは"Conditions"プロパティを使う方が適切であるように思います。

CloudFormationテンプレートのドキュメントはこちら↓
[AWS::Backup::BackupSelection BackupSelectionResourceType](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-backup-backupselection-backupselectionresourcetype.html)

AWS Backupについてはこちら↓
[AWS Backup データ保護の一元管理と自動化](https://aws.amazon.com/jp/backup/)

# "Conditions"と"ListOfTags"の違い

**＜Conditionsパラメータの説明＞**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531623/69726817-998f-c837-0ecd-893aedfd3074.png)

**＜ListOfTagsパラメータの説明＞**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531623/9b4ac3a8-44b9-a80d-4a23-04b49b54dc06.png)

直感的には"Conditions"より"ListOfTags"の方が「タグの条件を指定するよ！」という感じがしてわかりやすい気がします。

しかし、"ListOfTags"で複数のタグを指定した場合「OR条件」でいずれかのタグが一致するリソースが対象となります。 **つまり、複数タグの「AND条件」で絞り込みを行うイメージで"ListOfTags"を使ってしまうと、想定外の範囲のリソースまでバックアップ対象になってしまいます**。

一方で"Conditions"で指定しておけば「AND条件」となるので全てのタグ条件に一致するリソースのみが対象になり、私が期待していたのはこちらの動作でした。
AND条件、OR条件の他にも"ListOfTags"は「StringEquals」の条件しか書けないのに対し"Conditions"は「StringLike」などのより柔軟な条件も設定できる違いがあり、CloudFormationテンプレートの書き方も異なります。

通常はOR条件ではなくAND条件で対象の絞り込みをしたいことの方が多いと思います。  
しっかりとドキュメントを確認していれば防げるミスではありますが、"ListOfTags"はプロパティ名から仕様を勘違いしやすい気もしますし、個人的にはOR条件が必要なければ最初から"Conditions"だけを使っておいた方が余計な過ちを防げる気がします。

# テンプレート例

"Conditions"を利用したEC2インスタンスのバックアップ用リソースのテンプレート例を参考までに載せておきます。

```yaml
AWSTemplateFormatVersion: 2010-09-09
Description: >-
  Create AWS Backup Plan(sample).

Parameters:
  GroupName:
    Type: String
    Default: "sample-group"
    Description: "the 'GroupName' tag value of target instance"
  EnvName:
    Type: String
    Default: "dev"
    Description: "the 'EnvName' tag value of target instance"

Resources:
  BackupVault:
    Type: AWS::Backup::BackupVault
    Properties:
      BackupVaultName: !Sub ${AWS::StackName}-vault

  BackupPlan:
    Type: AWS::Backup::BackupPlan
    Properties:
      BackupPlan:
        BackupPlanName: !Sub ${AWS::StackName}-plan
        BackupPlanRule:
          - RuleName: !Sub ${AWS::StackName}-plan-rule
            TargetBackupVault: !Ref BackupVault
            # UTC setting required (JST -9h)
            ScheduleExpression: "cron(0 15 ? * * *)"
            StartWindowMinutes: 60
            CompletionWindowMinutes: 300
            Lifecycle:
              DeleteAfterDays: 3

  BackupSelectionEC2:
    Type: AWS::Backup::BackupSelection
    Properties:
      BackupPlanId: !Ref BackupPlan
      BackupSelection:
        SelectionName: !Sub ${AWS::StackName}-selection
        IamRoleArn: !GetAtt BackupRole.Arn
        Resources:
          - arn:aws:ec2:*:*:instance/*
        Conditions:
          StringEquals:
            - ConditionKey: "aws:ResourceTag/GroupName"
              ConditionValue: !Ref GroupName
            - ConditionKey: "aws:ResourceTag/EnvName"
              ConditionValue: !Ref EnvName

  BackupRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${AWS::StackName}-role
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
        - Effect: Allow
          Action: sts:AssumeRole
          Principal:
            Service: backup.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForBackup
        - arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForRestores
```

上のCloudFormationテンプレートでは、毎日0時(UTCの15時)にBackupSelectionで指定した条件に一致するリソースを3日分バックアップする設定のAWS Backup関連リソースが作成されます。
当記事に関連するポイントは下記の部分で、"GroupName"タグと"EnvName"タグの両方がパラメータで指定したタグの値と一致する「EC2インスタンスのリソース」のみにバックアップ対象を限定しています。
```yaml
        Resources:
          - arn:aws:ec2:*:*:instance/*
        Conditions:
          StringEquals:
            - ConditionKey: "aws:ResourceTag/GroupName"
              ConditionValue: !Ref GroupName
            - ConditionKey: "aws:ResourceTag/EnvName"
              ConditionValue: !Ref EnvName
```


下記参考URLに公式のサンプルもあります。
[参考: バックアッププランへのリソースの割り当て](https://docs.aws.amazon.com/ja_jp/aws-backup/latest/devguide/assigning-resources.html)

# まとめ

よくドキュメントを読まずに直感で"ListOfTags"でタグの絞り込み条件を指定してしまうと「え？！」という結果になり得ますのでご注意を。
複数タグを使った絞り込み条件には"Conditions"を使いましょう。