Amazon EC2 インスタンスを AWS CDK で構築する際のインスタンスプロファイル作成では grant メソッドを使うのが便利

2024.04.14

こんにちは、CX 事業本部製造ビジネステクノロジー部の若槻です。

AWS CDKで、AWS リソース間のアクセス権限を付与する際には grant メソッドを使うと、コードの記述量の削減や、より宣言的な記述ができるので便利です。

今回は、Amazon EC2インスタンスを AWS CDK で構築する際のインスタンスプロファイル作成でも grant メソッドを使うのが便利でしたのでご紹介します。

やってみた

例として EC2 インスタンス上にセットアップされた NICE CSV サーバーが、S3 バケットに保存されているライセンスファイルを読み取るための権限を付与する場合を考えます。

インスタンスプロファイルには次のポリシーの権限を設定する必要があります。

{
    "Version": "2012-10-17",
    "Statement": [
       {
           "Effect": "Allow",
           "Action": "s3:GetObject",
           "Resource": "arn:aws:s3:::dcv-license.region/*"
       }
    ]
}

サンプルコード

CDK スタック定義のサンプルコード(TypeScript)です。fromBucketName メソッドを使ってバケットのコンストラクトを取得し、インスタンスに grantRead メソッドで読み取り権限を付与しています。

lib/cdk-sample-stack.ts

import { aws_ec2, aws_s3, Stack } from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class CdkSampleStack extends Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    const vpcCidr = '10.100.0.0/16';
    const instanceAmiId = 'ami-0eba6c58b7918d3a1'; // Ubuntu Server 22.04 LTS (HVM), SSD Volume Type (64-bit (x86))
    const region = 'ap-northeast-1';

    // VPC を作成
    const vpc = new aws_ec2.Vpc(this, 'Vpc', {
      ipAddresses: aws_ec2.IpAddresses.cidr(vpcCidr),
      subnetConfiguration: [
        {
          cidrMask: 24,
          name: 'public',
          subnetType: aws_ec2.SubnetType.PUBLIC,
        },
      ],
      maxAzs: 1,
    });

    // セキュリティグループを作成
    const securityGroup = new aws_ec2.SecurityGroup(this, 'SecurityGroup', {
      vpc,
    });

    // インスタンスを作成
    const instance = new aws_ec2.Instance(this, 'Instance', {
      vpc,
      securityGroup,
      instanceType: aws_ec2.InstanceType.of(
        aws_ec2.InstanceClass.T3,
        aws_ec2.InstanceSize.SMALL
      ),
      machineImage: aws_ec2.MachineImage.genericLinux({
        [region]: instanceAmiId,
      }),
      requireImdsv2: true,
    });

    // バケットのコンストラクトを取得
    const dcvLicenseBucket = aws_s3.Bucket.fromBucketName(
      this,
      'DcvLicenseBucket',
      `dcv-license.${region}`
    );

    // インスタンスに S3 バケットへの読み取り権限を付与
    dcvLicenseBucket.grantRead(instance, "*");

    // インスタンスプロファイルのロール名を出力
    new CfnOutput(this, 'InstanceProfileRoleName', {
      value: instance.role.roleName,
    });
  }
}

dcvLicenseBucket.grantRead(instance, "*") という記述により、「インスタンスに * プレフィクスの読み取り権限を付与する」を可能にするため、コードの記述自体が実現したいことを表現しており、可読性が非常に高くなっていると感じます。

CDK デプロイを行い、作成されたインスタンスプロファイルのロールのポリシーを確認してみます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:GetBucket*",
                "s3:GetObject*",
                "s3:List*"
            ],
            "Resource": [
                "arn:aws:s3:::dcv-license.ap-northeast-1",
                "arn:aws:s3:::dcv-license.ap-northeast-1/*"
            ],
            "Effect": "Allow"
        }
    ]
}

NICE CSV サーバーのライセンス読み取りのための必要最低限の権限よりも若干多くの権限が付与されていることには注意が必要ですが、適切な範囲の権限が付与されていることが確認できました。

grant メソッドを使用しない場合

grant メソッドを使用せずに同様の権限を付与する場合の CDK コードです。記述内容の抽象化がされていないため、実現したい内容が直感的に理解しにくいと感じられます。

lib/cdk-sample-stack.ts

import { aws_ec2, aws_s3, aws_iam, Stack } from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class CdkSampleStack extends Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    const vpcCidr = '10.100.0.0/16';
    const instanceAmiId = 'ami-0eba6c58b7918d3a1'; // Ubuntu Server 22.04 LTS (HVM), SSD Volume Type (64-bit (x86))
    const region = 'ap-northeast-1';

    // VPC を作成
    const vpc = new aws_ec2.Vpc(this, 'Vpc', {
      ipAddresses: aws_ec2.IpAddresses.cidr(vpcCidr),
      subnetConfiguration: [
        {
          cidrMask: 24,
          name: 'public',
          subnetType: aws_ec2.SubnetType.PUBLIC,
        },
      ],
      maxAzs: 1,
    });

    // セキュリティグループを作成
    const securityGroup = new aws_ec2.SecurityGroup(this, 'SecurityGroup', {
      vpc,
    });

    // IAM ロールを作成
    const role = new aws_iam.Role(this, 'Role', {
      assumedBy: new aws_iam.ServicePrincipal('ec2.amazonaws.com'),
    });

    // // ロールにポリシーステートメントを追加
    role.addToPolicy(
      new aws_iam.PolicyStatement({
        actions: ['s3:GetObject'],
        resources: [`arn:aws:s3:::dcv-license.ap-northeast-1/*`],
      })
    );

    // インスタンスを作成
    new aws_ec2.Instance(this, 'Instance', {
      vpc,
      securityGroup,
      role,
      instanceType: aws_ec2.InstanceType.of(
        aws_ec2.InstanceClass.T3,
        aws_ec2.InstanceSize.SMALL
      ),
      machineImage: aws_ec2.MachineImage.genericLinux({
        [region]: instanceAmiId,
      }),
      requireImdsv2: true,
    });
  }
}

おわりに

Amazon EC2 インスタンスを AWS CDK で構築する際のインスタンスプロファイル作成では grant メソッドを使うのが便利という話でした。

grant メソッドについては、今までは Lambda 関数や Step Functions ステートマシンなどに対して権限を付与する際に使っていましたが、インスタンスプロファイルに対しても使うととても便利であることが分かりました。

参考

以上