[アップデート] リソースグループをCloudFormationで作成できるようになりました

AWSの各種リソースをグループ化して管理できる「リソースグループ」機能が、これまでのマネジメントコンソールを使う方法以外に、CloudFormationを使って作成できるようになりました。
2020.03.27

みなさん、こんにちは!
AWS事業本部の青柳@福岡オフィスです。

AWSには、各種リソースをグループ化して一括管理し易くする AWSリソースグループ という機能があります。

今までリソースグループはマネジメントコンソールかAWS CLIで作成していたのですが、今回のアップデートでCloudFormationを使って作成できるようになりました。

AWS Resource Groups now supports AWS CloudFormation

リソースグループとは

マネジメントコンソールの上部バーに常時表示されているココ↓からアクセスできます。

または、Systems Managerのメニューからもアクセス可能です。

リソースグループの機能や使い方について紹介したブログ記事がありますので、参考にしてください。

【新機能】複数のインスタンスのタグを一発で書き換える[Tag Editor]と共通タグをコレクション化する[Resource Groups] | Developers.IO
[AWS]リソースグループを使って、テスト・ステージング・本番環境ごとにAWSリソースを管理する | Developers.IO

また、最近ではSystems ManagerのRun Commandがリソースグループに対応したことで、より使い勝手が良くなっています。

Run Commandのターゲットとしてリソースグループが選択可能になりました | Developers.IO

リソースグループをCloudFormationで作成する

リソースグループは、グループ化するリソースの特定方法の違いにより「タグベース」と「CloudFormationスタックベース」の2種類があります。

それぞれの種類についてCloudFormationの記述方法を説明します。

タグベースのリソースグループを作成する場合

CloudFormationテンプレートの記述方法

リソースグループを作成する部分の記述は以下のようになります。

ResourceGroup:
  Type: AWS::ResourceGroups::Group
  Properties:
    Name: リソースグループの名前
    Description: リソースグループの説明
    ResourceQuery:
      Type: TAG_FILTERS_1_0
      Query:
        ResourceTypeFilters:
          - グループ化の対象とするリソースタイプを列挙
          - (例)
          - AWS::EC2::Instance
          - AWS::DynamoDB::Table
          - ・・・
        TagFilters:
          - Key: タグキー
            Values:
              - タグ値
    Tags:
      - (任意)リソースグループ自体に付けるタグを指定

タグベースの指定をする場合は、ResourceQuery の下の TypeTAG_FILTERS_1_0 と記述します。
(Type セクションを省略した場合も TAG_FILTERS_1_0 を指定したことになります)

Query セクション配下にグループ化の対象とするリソースの検索条件を記述します。

ResourceTypeFilters には、グループ化の対象とするリソースタイプを列挙します。
リソースタイプ AWS::AllSupported を指定した場合、または ResourceTypeFilters 自体を省略した場合は、リソースグループがサポートしている全てのリソースタイプが対象となります。
(リソースグループがサポートしているリソースタイプの一覧は こちら にあります)

TagFilters には、グループ化の対象リソースを検索するタグを「キー」「値」の組で指定します。
タグキー・値の組は複数列挙することもできます。
また、タグキーに対するタグ値も複数列挙できます。

CloudFormationでリソースグループを作成してみる

では、実際にCloudFormationでリソースグループを作成してみましょう。

リソースグループおよびVPC、EC2等のリソースを含むサンプルテンプレートを用意しました。

CloudFormationテンプレート (クリックすると展開します)

resource-group-tag-based.yaml

---
AWSTemplateFormatVersion: "2010-09-09"
Description: "Example template for creating VPC, EC2 and 'Tag based' Rsource Group"

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "General Information"
        Parameters:
          - SystemName
      - Label:
          default: "Network Configuration"
        Parameters:
          - CidrBlockVPC
          - CidrBlockSubnetPublic
      - Label:
          default: "EC2 Instance Configuration"
        Parameters:
          - EC2ImageID
          - EC2InstanceType
          - EC2VolumeType
          - EC2VolumeSize

Parameters:
  SystemName:
    Type: String
    Default: example1

  CidrBlockVPC:
    Type: String
    Default: 192.168.0.0/16

  CidrBlockSubnetPublic:
    Type: String
    Default: 192.168.0.0/24

  EC2ImageID:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2

  EC2InstanceType:
    Type: String
    Default: t3.micro

  EC2VolumeType:
    Type: String
    Default: gp2

  EC2VolumeSize:
    Type: String
    Default: 8

Resources:
  ResourceGroup:
    Type: AWS::ResourceGroups::Group
    Properties:
      Name: !Sub "${SystemName}-rg"
      Description: !Sub "Resource Group for ${SystemName} system"
      ResourceQuery:
        Type: TAG_FILTERS_1_0
        Query:
          ResourceTypeFilters:
            - AWS::AllSupported
          TagFilters:
            - Key: System
              Values:
                - !Ref SystemName
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-rg"
        - Key: System
          Value: !Ref SystemName

  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref CidrBlockVPC
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-vpc"
        - Key: System
          Value: !Ref SystemName

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-igw"
        - Key: System
          Value: !Ref SystemName

  VPCGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

  SubnetPublic:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select
        - 0
        - Fn::GetAZs: !Ref AWS::Region
      CidrBlock: !Ref CidrBlockSubnetPublic
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-public-subnet"
        - Key: System
          Value: !Ref SystemName

  RouteTablePublic:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-public-rtb"
        - Key: System
          Value: !Ref SystemName

  RouteIGW:
    DependsOn:
      - VPCGatewayAttachment
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref RouteTablePublic
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  RouteTableAssociationPublic:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SubnetPublic
      RouteTableId: !Ref RouteTablePublic

  SecurityGroupWeb:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub "${SystemName}-web-sg"
      GroupDescription: "Security group for Web Server"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-web-sg"
        - Key: System
          Value: !Ref SystemName

  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref EC2ImageID
      InstanceType: !Ref EC2InstanceType
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeType: !Ref EC2VolumeType
            VolumeSize: !Ref EC2VolumeSize
      NetworkInterfaces:
        - DeviceIndex: 0
          SubnetId: !Ref SubnetPublic
          GroupSet:
            - !Ref SecurityGroupWeb
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-ec2"
        - Key: System
          Value: !Ref SystemName

Outputs:
  ResourceGroup:
    Value: !Ref ResourceGroup
    Export:
      Name: !Sub "${AWS::StackName}::ResourceGroup"

  VPC:
    Value: !Ref VPC
    Export:
      Name: !Sub "${AWS::StackName}::VPC"

  SubnetPublic:
    Value: !Ref SubnetPublic
    Export:
      Name: !Sub "${AWS::StackName}::SubnetPublic"

  SecurityGroupWeb:
    Value: !Ref SecurityGroupWeb
    Export:
      Name: !Sub "${AWS::StackName}::SecurityGroupWeb"

  EC2Instance:
    Value: !Ref EC2Instance
    Export:
      Name: !Sub "${AWS::StackName}::EC2Instance"

このCloudFormationテンプレートを使って作成されたリソースグループの内容を確認します。

「キー:System、値:example1」のタグが設定されている各リソースが、リソースグループでグループ化されました。

今回はリソースグループ自身にもタグを設定しているため、リソースグループ自体もグループに含まれていることに注目してください。

CloudFormationスタックベースのリソースグループを作成する場合

CloudFormationテンプレートの記述方法

リソースグループを作成する部分の記述は以下のようになります。

ResourceGroup:
  Type: AWS::ResourceGroups::Group
  Properties:
    Name: リソースグループの名前
    Description: リソースグループの説明
    ResourceQuery:
      Type: CLOUDFORMATION_STACK_1_0
      Query:
        ResourceTypeFilters:
          - グループ化の対象とするリソースタイプを列挙
          - (例)
          - AWS::EC2::Instance
          - AWS::DynamoDB::Table
          - ・・・
          StackIdentifier: CloudFormationスタックのARN
    Tags:
      - (任意)リソースグループ自体に付けるタグを指定

CloudFormationスタックベースの指定をする場合は、ResourceQuery の下の TypeCLOUDFORMATION_STACK_1_0 と記述します。
(省略はできません)

Query セクション配下にグループ化の対象とするリソースの検索条件を記述します。

ResourceTypeFilters の指定方法は、タグベースの場合と同様です。

StackIdentifier には、グループ化の対象とするCloudFormationスタックをARNで指定します。

CloudFormationでリソースグループを作成してみる

こちらも、リソースグループおよびVPC、EC2等のリソースを含むサンプルテンプレートを用意しました。

CloudFormationテンプレート (クリックすると展開します)

resource-group-tag-based.yaml

---
AWSTemplateFormatVersion: "2010-09-09"
Description: "Example template for creating VPC, EC2 and 'CloudFromation stack based' Rsource Group"

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "General Information"
        Parameters:
          - SystemName
      - Label:
          default: "Network Configuration"
        Parameters:
          - CidrBlockVPC
          - CidrBlockSubnetPublic
      - Label:
          default: "EC2 Instance Configuration"
        Parameters:
          - EC2ImageID
          - EC2InstanceType
          - EC2VolumeType
          - EC2VolumeSize

Parameters:
  SystemName:
    Type: String
    Default: example2

  CidrBlockVPC:
    Type: String
    Default: 192.168.0.0/16

  CidrBlockSubnetPublic:
    Type: String
    Default: 192.168.0.0/24

  EC2ImageID:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2

  EC2InstanceType:
    Type: String
    Default: t3.micro

  EC2VolumeType:
    Type: String
    Default: gp2

  EC2VolumeSize:
    Type: String
    Default: 8

Resources:
  ResourceGroup:
    Type: AWS::ResourceGroups::Group
    Properties:
      Name: !Sub "${SystemName}-rg"
      Description: !Sub "Resource Group for ${SystemName} system"
      ResourceQuery:
        Type: CLOUDFORMATION_STACK_1_0
        Query:
          ResourceTypeFilters:
            - AWS::AllSupported
          StackIdentifier: !Ref AWS::StackId
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-rg"
        - Key: System
          Value: !Ref SystemName

  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref CidrBlockVPC
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-vpc"
        - Key: System
          Value: !Ref SystemName

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-igw"
        - Key: System
          Value: !Ref SystemName

  VPCGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

  SubnetPublic:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select
        - 0
        - Fn::GetAZs: !Ref AWS::Region
      CidrBlock: !Ref CidrBlockSubnetPublic
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-public-subnet"
        - Key: System
          Value: !Ref SystemName

  RouteTablePublic:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-public-rtb"
        - Key: System
          Value: !Ref SystemName

  RouteIGW:
    DependsOn:
      - VPCGatewayAttachment
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref RouteTablePublic
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  RouteTableAssociationPublic:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SubnetPublic
      RouteTableId: !Ref RouteTablePublic

  SecurityGroupWeb:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub "${SystemName}-web-sg"
      GroupDescription: "Security group for Web Server"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-web-sg"
        - Key: System
          Value: !Ref SystemName

  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref EC2ImageID
      InstanceType: !Ref EC2InstanceType
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeType: !Ref EC2VolumeType
            VolumeSize: !Ref EC2VolumeSize
      NetworkInterfaces:
        - DeviceIndex: 0
          SubnetId: !Ref SubnetPublic
          GroupSet:
            - !Ref SecurityGroupWeb
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-ec2"
        - Key: System
          Value: !Ref SystemName

Outputs:
  ResourceGroup:
    Value: !Ref ResourceGroup
    Export:
      Name: !Sub "${AWS::StackName}::ResourceGroup"

  VPC:
    Value: !Ref VPC
    Export:
      Name: !Sub "${AWS::StackName}::VPC"

  SubnetPublic:
    Value: !Ref SubnetPublic
    Export:
      Name: !Sub "${AWS::StackName}::SubnetPublic"

  SecurityGroupWeb:
    Value: !Ref SecurityGroupWeb
    Export:
      Name: !Sub "${AWS::StackName}::SecurityGroupWeb"

  EC2Instance:
    Value: !Ref EC2Instance
    Export:
      Name: !Sub "${AWS::StackName}::EC2Instance"

StackIdentifier: !Ref AWS::StackId と記述することで、実行しているスタックの自分自身を指定している点がポイントです。

作成されたリソースグループの内容を確認します。

CloudFormationスタックで作成された各リソースが、リソースグループでグループ化されました。

こちらの場合では、リソースグループ自体はグループに含まれていません。
CloudFormationスタックベースのリソースグループでは、リソースタイプ「リソースグループ」がサポートされていないためです。

おわりに

CloudFormationでリソースグループを作成する方法をご紹介しました。

管理の面からリソースグループと各リソースは別々に作成した方がよい場合もあるかと思いますが、一度に作成したい場合には便利になったのではないかと思います。