都度IAMロール定義するの面倒だな
こんにちは、のんピ(@non____97)です。
皆さんはAWS CDKで検証用のEC2インスタンスを立てるときにAmazonSSMManagedInstanceCoreがアタッチされたIAMロールを定義するのが面倒だなと思ったことはありますか? 私はあります。
AWS CDK 2.70.0からssmSessionPermissions
をtrue
とすることでEC2インスタンスに割り当てられたIAMロールにAmazonSSMManagedInstanceCoreを簡単にアタッチできるようになりました。
これにより、AWS SSM Default Host Management Configurationが有効にできない環境において、より簡単にEC2インスタンスにSSMセッションマネージャーで接続できるようになります。
AWS SSM Default Host Management Configurationの詳細は以下記事をご覧ください。
検証用のEC2インスタンスをサクッと立てたい時に非常に役立ちそうです。
実際に試してみたので紹介します。
やってみた
ssmSessionPermissions を指定しない場合
比較用にssmSessionPermissions
を指定しない場合を確認します。
以下のようなEC2インスタンスを1台Public Subnetにデプロイするスタックを定義します。
./lib/ec2-stack.ts
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
export class Ec2Stack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// VPC
const vpc = new cdk.aws_ec2.Vpc(this, "Vpc", {
ipAddresses: cdk.aws_ec2.IpAddresses.cidr("10.1.1.0/24"),
enableDnsHostnames: true,
enableDnsSupport: true,
natGateways: 0,
maxAzs: 1,
subnetConfiguration: [
{
name: "Public",
subnetType: cdk.aws_ec2.SubnetType.PUBLIC,
cidrMask: 27,
mapPublicIpOnLaunch: true,
},
],
});
// EC2 Instance
new cdk.aws_ec2.Instance(this, "Ec2 Instance", {
machineImage: cdk.aws_ec2.MachineImage.fromSsmParameter(
"/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64"
),
instanceType: new cdk.aws_ec2.InstanceType("t3.micro"),
vpc,
vpcSubnets: vpc.selectSubnets({
subnetGroupName: "Public",
}),
propagateTagsToVolumeOnCreation: true,
// ssmSessionPermissions: true,
});
}
}
cdk deploy
する前にcdk diff
をしてどんなリソースが作成されるのか確認します。
npx cdk diff
Stack Ec2Stack
IAM Statement Changes
┌───┬──────────────────────────────────┬────────┬────────────────┬───────────────────────────┬───────────┐
│ │ Resource │ Effect │ Action │ Principal │ Condition │
├───┼──────────────────────────────────┼────────┼────────────────┼───────────────────────────┼───────────┤
│ + │ ${Ec2 Instance/InstanceRole.Arn} │ Allow │ sts:AssumeRole │ Service:ec2.amazonaws.com │ │
└───┴──────────────────────────────────┴────────┴────────────────┴───────────────────────────┴───────────┘
Security Group Changes
┌───┬───────────────────────────────────────────────┬─────┬────────────┬─────────────────┐
│ │ Group │ Dir │ Protocol │ Peer │
├───┼───────────────────────────────────────────────┼─────┼────────────┼─────────────────┤
│ + │ ${Ec2 Instance/InstanceSecurityGroup.GroupId} │ Out │ Everything │ Everyone (IPv4) │
└───┴───────────────────────────────────────────────┴─────┴────────────┴─────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Parameters
[+] Parameter SsmParameterValue:--aws--service--ami-amazon-linux-latest--al2023-ami-kernel-6.1-x86_64:C96584B6-F00A-464E-AD19-53AFF4B05118.Parameter SsmParameterValueawsserviceamiamazonlinuxlatestal2023amikernel61x8664C96584B6F00A464EAD1953AFF4B05118Parameter: {"Type":"AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>","Default":"/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64"}
[+] Parameter BootstrapVersion BootstrapVersion: {"Type":"AWS::SSM::Parameter::Value<String>","Default":"/cdk-bootstrap/hnb659fds/version","Description":"Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"}
Conditions
[+] Condition CDKMetadata/Condition CDKMetadataAvailable: {"Fn::Or":[{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"af-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ca-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-northwest-1"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-3"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"sa-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-2"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-2"]}]}]}
Resources
[+] AWS::EC2::VPC Vpc Vpc8378EB38
[+] AWS::EC2::Subnet Vpc/PublicSubnet1/Subnet VpcPublicSubnet1Subnet5C2D37C4
[+] AWS::EC2::RouteTable Vpc/PublicSubnet1/RouteTable VpcPublicSubnet1RouteTable6C95E38E
[+] AWS::EC2::SubnetRouteTableAssociation Vpc/PublicSubnet1/RouteTableAssociation VpcPublicSubnet1RouteTableAssociation97140677
[+] AWS::EC2::Route Vpc/PublicSubnet1/DefaultRoute VpcPublicSubnet1DefaultRoute3DA9E72A
[+] AWS::EC2::InternetGateway Vpc/IGW VpcIGWD7BA715C
[+] AWS::EC2::VPCGatewayAttachment Vpc/VPCGW VpcVPCGWBF912B6E
[+] AWS::EC2::SecurityGroup Ec2 Instance/InstanceSecurityGroup Ec2InstanceInstanceSecurityGroupDFF556AA
[+] AWS::IAM::Role Ec2 Instance/InstanceRole Ec2InstanceInstanceRole8E810D82
[+] AWS::IAM::InstanceProfile Ec2 Instance/InstanceProfile Ec2InstanceInstanceProfileA536E794
[+] AWS::EC2::Instance Ec2 Instance Ec2Instance1F7BF384
Other Changes
[+] Unknown Rules: {"CheckBootstrapVersion":{"Assertions":[{"Assert":{"Fn::Not":[{"Fn::Contains":[["1","2","3","4","5"],{"Ref":"BootstrapVersion"}]}]},"AssertDescription":"CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."}]}}
どうやらIAMロールとインスタンスプロファイルは指定しなくても作成されるようですね。
cdk deploy
後作成されたEC2インスタンスを確認します。
インスタンスプロファイルがアタッチされていますね。
それでは、このインスタンスプロファイルのIAMロールにアタッチされているIAMポリシーを確認します。
はい、何もアタッチされていないようです。
AWS CLIからも確認しましょう
# IAMロールの確認
$ aws iam get-role --role-name Ec2Stack-Ec2InstanceInstanceRole8E810D82-MG2MR00NHQRU
{
"Role": {
"Path": "/",
"RoleName": "Ec2Stack-Ec2InstanceInstanceRole8E810D82-MG2MR00NHQRU",
"RoleId": "AROA6KUFAVPUUYMVBFMEL",
"Arn": "arn:aws:iam::<AWSアカウントID>:role/Ec2Stack-Ec2InstanceInstanceRole8E810D82-MG2MR00NHQRU",
"CreateDate": "2023-03-25T02:03:35+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"Description": "",
"MaxSessionDuration": 3600,
"Tags": [
{
"Key": "Name",
"Value": "Ec2Stack/Ec2 Instance"
}
],
"RoleLastUsed": {}
}
}
# IAMロールにアタッチされているIAMポリシーの確認
$ aws iam list-attached-role-policies --role-name Ec2Stack-Ec2InstanceInstanceRole8E810D82-MG2MR00NHQRU
{
"AttachedPolicies": []
}
# IAMロールのインスタンスプロファイルの確認
$ aws iam get-instance-profile --instance-profile-name Ec2Stack-Ec2InstanceInstanceProfileA536E794-if1RdbarivZR
{
"InstanceProfile": {
"Path": "/",
"InstanceProfileName": "Ec2Stack-Ec2InstanceInstanceProfileA536E794-if1RdbarivZR",
"InstanceProfileId": "AIPA6KUFAVPU4PEYXCPDC",
"Arn": "arn:aws:iam::<AWSアカウントID>:instance-profile/Ec2Stack-Ec2InstanceInstanceProfileA536E794-if1RdbarivZR",
"CreateDate": "2023-03-25T02:03:52+00:00",
"Roles": [
{
"Path": "/",
"RoleName": "Ec2Stack-Ec2InstanceInstanceRole8E810D82-MG2MR00NHQRU",
"RoleId": "AROA6KUFAVPUUYMVBFMEL",
"Arn": "arn:aws:iam::<AWSアカウントID>:role/Ec2Stack-Ec2InstanceInstanceRole8E810D82-MG2MR00NHQRU",
"CreateDate": "2023-03-25T02:03:35+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
}
],
"Tags": []
}
}
SSMセッションマネージャーでこちらのEC2インスタンスに接続できるかも確認します。
$ aws ssm start-session \
--target i-0b963c41f2fc17f60
An error occurred (TargetNotConnected) when calling the StartSession operation: i-0b963c41f2fc17f60 is not connected.
接続できないと怒られましたね。
ssmSessionPermissions を true にした場合
それではssmSessionPermissions
をtrue
にしてデプロイしてみます。
./lib/ec2-stack.ts
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
export class Ec2Stack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// VPC
const vpc = new cdk.aws_ec2.Vpc(this, "Vpc", {
ipAddresses: cdk.aws_ec2.IpAddresses.cidr("10.1.1.0/24"),
enableDnsHostnames: true,
enableDnsSupport: true,
natGateways: 0,
maxAzs: 1,
subnetConfiguration: [
{
name: "Public",
subnetType: cdk.aws_ec2.SubnetType.PUBLIC,
cidrMask: 27,
mapPublicIpOnLaunch: true,
},
],
});
// EC2 Instance
new cdk.aws_ec2.Instance(this, "Ec2 Instance", {
machineImage: cdk.aws_ec2.MachineImage.fromSsmParameter(
"/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64"
),
instanceType: new cdk.aws_ec2.InstanceType("t3.micro"),
vpc,
vpcSubnets: vpc.selectSubnets({
subnetGroupName: "Public",
}),
propagateTagsToVolumeOnCreation: true,
ssmSessionPermissions: true,
});
}
}
cdk diff
をすると、IAMロールへのAmazonSSMManagedInstanceCoreのアタッチが差分として表示されました。
$ npx cdk diff
Stack Ec2Stack
IAM Policy Changes
┌───┬──────────────────────────────┬────────────────────────────────────────────────────────────────────┐
│ │ Resource │ Managed Policy ARN │
├───┼──────────────────────────────┼────────────────────────────────────────────────────────────────────┤
│ + │ ${Ec2 Instance/InstanceRole} │ arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore │
└───┴──────────────────────────────┴────────────────────────────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Resources
[~] AWS::IAM::Role Ec2 Instance/InstanceRole Ec2InstanceInstanceRole8E810D82
└─ [+] ManagedPolicyArns
└─ [{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::aws:policy/AmazonSSMManagedInstanceCore"]]}]
cdk deplory
後、IAMロールにアタッチされているIAMポリシーを確認します。
$ aws iam list-attached-role-policies --role-name Ec2Stack-Ec2InstanceInstanceRole8E810D82-MG2MR00NHQRU
{
"AttachedPolicies": [
{
"PolicyName": "AmazonSSMManagedInstanceCore",
"PolicyArn": "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
]
}
確かにIAMロールにAmazonSSMManagedInstanceCoreがアタッチされていますね。
SSMセッションマネージャーで接続できるかも確認します。
$ aws ssm start-session \
--target i-0b963c41f2fc17f60
Starting session with SessionId: botocore-session-1679710533-0f34a9bcfe619372d
$ whoami
ssm-user
$ hostname
ip-10-1-1-14.ec2.internal
$ cat /etc/os-release
NAME="Amazon Linux"
VERSION="2023"
ID="amzn"
ID_LIKE="fedora"
VERSION_ID="2023"
PLATFORM_ID="platform:al2023"
PRETTY_NAME="Amazon Linux 2023"
ANSI_COLOR="0;33"
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2023"
HOME_URL="https://aws.amazon.com/linux/"
BUG_REPORT_URL="https://github.com/amazonlinux/amazon-linux-2023"
SUPPORT_END="2028-03-01"
接続できましたね。これは楽です。
AmazonSSMManagedInstanceCoreがアタッチされていないIAMロールの指定 かつ ssmSessionPermissions を true にした場合
「AmazonSSMManagedInstanceCoreがアタッチされていないIAMロールの指定」かつ「ssmSessionPermissions
をtrue
」にした場合、IAMロールにAmazonSSMManagedInstanceCoreがアタッチされるかも確認します。
以下のようにssmSessionPermissions
をtrue
にした状態で、IAMポリシーを何もアタッチしていないIAMロールをEC2インスタンスに割り当てます。
./lib/ec2-stack.ts
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
export class Ec2Stack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// IAM Role
const iamRole = new cdk.aws_iam.Role(this, "Iam Role", {
assumedBy: new cdk.aws_iam.ServicePrincipal("ec2.amazonaws.com"),
});
// VPC
const vpc = new cdk.aws_ec2.Vpc(this, "Vpc", {
ipAddresses: cdk.aws_ec2.IpAddresses.cidr("10.1.1.0/24"),
enableDnsHostnames: true,
enableDnsSupport: true,
natGateways: 0,
maxAzs: 1,
subnetConfiguration: [
{
name: "Public",
subnetType: cdk.aws_ec2.SubnetType.PUBLIC,
cidrMask: 27,
mapPublicIpOnLaunch: true,
},
],
});
// EC2 Instance
new cdk.aws_ec2.Instance(this, "Ec2 Instance", {
machineImage: cdk.aws_ec2.MachineImage.fromSsmParameter(
"/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64"
),
instanceType: new cdk.aws_ec2.InstanceType("t3.micro"),
vpc,
vpcSubnets: vpc.selectSubnets({
subnetGroupName: "Public",
}),
propagateTagsToVolumeOnCreation: true,
ssmSessionPermissions: true,
role: iamRole,
});
}
}
cdk diff
をすると、どうやらAmazonSSMManagedInstanceCoreをアタッチするような差分が表示されました。
npx cdk diff
Stack Ec2Stack
IAM Statement Changes
┌───┬─────────────────┬────────┬────────────────┬───────────────────────────┬───────────┐
│ │ Resource │ Effect │ Action │ Principal │ Condition │
├───┼─────────────────┼────────┼────────────────┼───────────────────────────┼───────────┤
│ + │ ${Iam Role.Arn} │ Allow │ sts:AssumeRole │ Service:ec2.amazonaws.com │ │
└───┴─────────────────┴────────┴────────────────┴───────────────────────────┴───────────┘
IAM Policy Changes
┌───┬─────────────┬────────────────────────────────────────────────────────────────────┐
│ │ Resource │ Managed Policy ARN │
├───┼─────────────┼────────────────────────────────────────────────────────────────────┤
│ + │ ${Iam Role} │ arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore │
└───┴─────────────┴────────────────────────────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Resources
[-] AWS::IAM::Role Ec2InstanceInstanceRole8E810D82 destroy
[+] AWS::IAM::Role Iam Role IamRoleB5F0CB96
[~] AWS::IAM::InstanceProfile Ec2 Instance/InstanceProfile Ec2InstanceInstanceProfileA536E794
└─ [~] Roles
└─ @@ -1,5 +1,5 @@
[ ] [
[ ] {
[-] "Ref": "Ec2InstanceInstanceRole8E810D82"
[+] "Ref": "IamRoleB5F0CB96"
[ ] }
[ ] ]
[~] AWS::EC2::Instance Ec2 Instance Ec2Instance1F7BF384
└─ [~] DependsOn
└─ @@ -1,3 +1,3 @@
[ ] [
[-] "Ec2InstanceInstanceRole8E810D82"
[+] "IamRoleB5F0CB96"
cdk deploy
後EC2インスタンスにアタッチされているIAMロールを確認すると、AmazonSSMManagedInstanceCoreがアタッチされていることが分かりました。
AWS CDKのEC2インスタンスのConstructのコードを確認するとssmSessionPermissions
がtrue
の場合はIAMロールにアタッチしているIAMポリシーをAmazonSSMManagedInstanceCoreで上書きするのではなく、AmazonSSMManagedInstanceCoreを追加する処理をしていました。
aws-cdk/packages/@aws-cdk/aws-ec2/lib/instance.ts
this.role = props.role || new iam.Role(this, 'InstanceRole', {
assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'),
});
this.grantPrincipal = this.role;
if (props.ssmSessionPermissions) {
this.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'));
}
const iamProfile = new iam.CfnInstanceProfile(this, 'InstanceProfile', {
roles: [this.role.roleName],
});
そのため、AmazonSSMManagedInstanceCoreのアタッチはssmSessionPermissions
によってコントロールするといったこともできますね。
検証用にAWS CDKでEC2インスタンスを構築する時ちょっと楽になりました
AWS CDKでEC2インスタンスに割り当てられたIAMロールにAmazonSSMManagedInstanceCoreを簡単にアタッチできるようになったアップデートを紹介しました。
検証用にAWS CDKでEC2インスタンスを構築する時ちょっと楽になりましたね。AWS SSM Default Host Management Configurationが有効にできない環境で検証する場合は使っていこうと思います。
この記事が誰かの助けになれば幸いです。
以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!