
AWS CDK(v2)で複数EC2を作ってIAM Role(Instance Profile)を共用する
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
AWS CDKでEC2を作るときにIAM Roleを共用したい!なんてことはありませんか? 僕はしょっちゅうあります。
そんなわけで、本ブログではAWS CDK(v2)でEC2を作るときにIAM Role(Instance Profile)を共用する方法を紹介します。
前提条件
AWS CDKはv2を利用しています。
$ cdk --version 2.79.1 (build 2e7f8b7)
CDKでIAM Roleを共用したEC2を作ってみる
まずは、さくっとTypeScriptのcdkでIAM Roleを共用するEC2を作ってみます。
次のプログラムを保存して。
import {Construct} from 'constructs';
import * as cdk from "aws-cdk-lib";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as iam from "aws-cdk-lib/aws-iam";
export class SampleStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const vpc = new ec2.Vpc(this, 'MyVpc', {
natGateways: 0,
maxAzs: 1,
subnetConfiguration: [
{
name: 'PublicSubnet',
subnetType: ec2.SubnetType.PUBLIC,
},
],
});
const ec2Role = new iam.Role(this, 'MyRole', {
assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'),
description: 'My EC2 role',
});
const instance1 = new ec2.Instance(this, 'MyInstance1', {
vpc,
instanceType: ec2.InstanceType.of(
ec2.InstanceClass.T3A,
ec2.InstanceSize.NANO
),
machineImage: ec2.MachineImage.latestAmazonLinux2(),
role: ec2Role,
vpcSubnets: {
subnetType: ec2.SubnetType.PUBLIC,
},
});
const instance2 = new ec2.Instance(this, 'MyInstance2', {
vpc,
instanceType: ec2.InstanceType.of(
ec2.InstanceClass.T3A,
ec2.InstanceSize.NANO
),
machineImage: ec2.MachineImage.latestAmazonLinux2(),
role: ec2Role,
vpcSubnets: {
subnetType: ec2.SubnetType.PUBLIC,
},
});
}
}
const app = new cdk.App();
new SampleStack(app, "SampleStack");
次のコマンドを実行してデプロイすると、EC2が2つ構築できます。簡単ですね。
$ cdk deploy SampleStack
CDKでIAM Roleを共用したEC2を作るときの問題点
EC2は2つ構築できたのでこのままでも良いのですが、ちょっと気持ち悪い点があります。
デプロイする前に、cdk diff SampleStack してみるとわかるのですが、InstanceProfileがEC2ごとに2つ作られていることがわかります。
$ cdk diff SampleStack Resources [+] AWS::EC2::VPC MyVpc MyVpcF9F0CA6F [+] AWS::EC2::Subnet MyVpc/PublicSubnetSubnet1/Subnet MyVpcPublicSubnetSubnet1Subnet60D1320D [+] AWS::EC2::RouteTable MyVpc/PublicSubnetSubnet1/RouteTable MyVpcPublicSubnetSubnet1RouteTable00654ADB [+] AWS::EC2::SubnetRouteTableAssociation MyVpc/PublicSubnetSubnet1/RouteTableAssociation MyVpcPublicSubnetSubnet1RouteTableAssociation2CCE9CDC [+] AWS::EC2::Route MyVpc/PublicSubnetSubnet1/DefaultRoute MyVpcPublicSubnetSubnet1DefaultRoute2D379878 [+] AWS::EC2::InternetGateway MyVpc/IGW MyVpcIGW5C4A4F63 [+] AWS::EC2::VPCGatewayAttachment MyVpc/VPCGW MyVpcVPCGW488ACE0D [+] AWS::IAM::Role MyRole MyRoleF48FFE04 [+] AWS::EC2::SecurityGroup MyInstance1/InstanceSecurityGroup MyInstance1InstanceSecurityGroupA4C82073 [+] AWS::IAM::InstanceProfile MyInstance1/InstanceProfile MyInstance1InstanceProfile2553CF04 [+] AWS::EC2::Instance MyInstance1 MyInstance12C7E210C [+] AWS::EC2::SecurityGroup MyInstance2/InstanceSecurityGroup MyInstance2InstanceSecurityGroup4974D913 [+] AWS::IAM::InstanceProfile MyInstance2/InstanceProfile MyInstance2InstanceProfile948D2D68 [+] AWS::EC2::Instance MyInstance2 MyInstance2C26007AF
普段意識することはあまりないのですが、EC2はIAM Roleを直接利用しているわけではなく、InstanceProfileを利用しています。 詳しくはこちらのブログを参照してください。
せっかくIAM Roleを共用したのだから、InstanceProfileも共用したいと思いません??だって、普段EC2ごとにInstanceProfile作らないですよね??
InstanceProfileは無料なんだから、複数作られたところで気にしなくても良いじゃん。 という、意見は無視して先に進みます。
長い前フリでしたが、InstanceProfileも共用するようにCDKを書いてみる方法を共有します。
CDKでInstance Profileを共用したEC2を作ってみる
cdkのInstanceクラスには、InstanceProfileを指定するプロパティがありません。
InstanceProfileは自動で作られてしまうので、 tryRemoveChild を使ってInstanceProfileの情報を消した後、
addPropertyOverride を使って、InstanceProfileを書き換えて対応しています。
また、インスタンスを作る前に、InstanceProfileを作っておく必要があるので、 addDependency を使って、CloudFormationの依存関係を作っています。
import {Construct} from 'constructs';
import * as cdk from "aws-cdk-lib";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as iam from "aws-cdk-lib/aws-iam";
export class SampleStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const vpc = new ec2.Vpc(this, 'MyVpc', {
natGateways: 0,
maxAzs: 1,
subnetConfiguration: [
{
name: 'PublicSubnet',
subnetType: ec2.SubnetType.PUBLIC,
},
],
});
const ec2Role = new iam.Role(this, 'MyRole', {
assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'),
description: 'My EC2 role',
});
const cfnInstanceProfile = new iam.CfnInstanceProfile(this, 'MyInstanceProfile', {
roles: [
ec2Role.roleName,
],
});
const instance1 = new ec2.Instance(this, 'MyInstance1', {
vpc,
instanceType: ec2.InstanceType.of(
ec2.InstanceClass.T3A,
ec2.InstanceSize.NANO
),
machineImage: ec2.MachineImage.latestAmazonLinux2(),
role: ec2Role,
vpcSubnets: {
subnetType: ec2.SubnetType.PUBLIC,
},
});
instance1.node.tryRemoveChild('InstanceProfile');
const cfnInstance1 = instance1.node.tryFindChild('Resource') as ec2.CfnInstance;
cfnInstance1.addDependency(cfnInstanceProfile);
cfnInstance1.addPropertyOverride('IamInstanceProfile', cfnInstanceProfile.ref);
const instance2 = new ec2.Instance(this, 'MyInstance2', {
vpc,
instanceType: ec2.InstanceType.of(
ec2.InstanceClass.T3A,
ec2.InstanceSize.NANO
),
machineImage: ec2.MachineImage.latestAmazonLinux2(),
role: ec2Role,
vpcSubnets: {
subnetType: ec2.SubnetType.PUBLIC,
},
});
instance2.node.tryRemoveChild('InstanceProfile');
const cfnInstance2 = instance2.node.tryFindChild('Resource') as ec2.CfnInstance;
cfnInstance2.addDependency(cfnInstanceProfile);
cfnInstance2.addPropertyOverride('IamInstanceProfile', cfnInstanceProfile.ref);
}
}
const app = new cdk.App();
new SampleStack(app, "SampleStack");
これで、InstanceProfileを共用したEC2が作れました。
できれば、CDKのAPIを使って、もっと簡単に書けるようになるといいですね。







