はじめに
生成AIの業務利用が進んでいる昨今、CloudFormationやTerraformといったIaCテンプレート作成への活用も耳にすることが増えました。
筆者自身、CloudFormationやTerraformのIaCテンプレートを生成AIの助けを得て作成することもかなり多いです。定義を間違えることも時々ありますが、複数リソース間での論理参照をやってくれたり結構便利で助かっています。
ところで、CloudFormationのツールとしてRainというものがあります。このRain、生成AIによるテンプレート作成機能を持っているとのことです。
ということで今回はRainの持つ生成AIテンプレート作成機能を試してみます。
Rainとは
Rainは、AWS CloudFormationのテンプレートとスタックを操作するためのCLIツールです。
CloudFormationのStackの参照やデプロイ、Template操作の拡張的な機能を利用可能で、Stackを作成するプロセスで役立つことも多そうです。
AWSのリポジトリで開発されています。ツール自体は生成AIを前提としているものではなく、v1.0は4年前にリリースされていました。
試してみる
インストール
今回はバージニア北部リージョンのAWS CloudShellで動作確認を行います。
生成AI機能ではバックグラウンドでBedrockの基盤モデルを利用するため、Bedrockの基盤モデルが有効化されているリージョンかつ利用権限のあるユーザで実行してください。
はじめにリリースノートから最新のバージョンのバイナリをダウンロードします。
今回は以下コマンドで1.8.1をダウンロードし、解凍しました。パスの追加も行います。
$ wget https://github.com/aws-cloudformation/rain/releases/download/v1.8.1/rain-v1.8.1_linux-amd64.zip
$ unzip rain-v1.8.1_linux-amd64.zip
$ export PATH=$PATH:~/rain-v1.8.1_linux-amd64/
rain
コマンドでヘルプが出ればOKです。
$ rain -h
Rain is a command line tool for working with AWS CloudFormation templates and stacks
Usage:
rain [command]
Stack commands:
cat Get the CloudFormation template from a running stack
cc Interact with templates using Cloud Control API instead of CloudFormation
deploy Deploy a CloudFormation stack or changeset from a local template
logs Show the event log for the named stack
ls List running CloudFormation stacks or changesets
rm Delete a CloudFormation stack or changeset
stackset This command manipulates stack sets.
watch Display an updating view of a CloudFormation stack
Template commands:
bootstrap Creates the artifacts bucket
build Create CloudFormation templates
diff Compare CloudFormation templates
fmt Format CloudFormation templates
forecast Predict deployment failures
merge Merge two or more CloudFormation templates
pkg Package local artifacts into a template
tree Find dependencies of Resources and Outputs in a local template
Other Commands:
completion Generate the autocompletion script for the specified shell
console Login to the AWS console
help Help about any command
info Show your current configuration
Rainが利用しているプロファイル情報はrain info
で確認することができます。以下のように表示されます。
$ rain info
Account: 000000000000
Region: us-east-1
Identity: arn:aws:iam::000000000000:user/username
AIによるCfnテンプレート生成
動作確認
Cfnテンプレート生成にはrain build
を使います。
生成AIに対してプロンプトを利用する場合は-p
オプションです。
まずは単純な指示を試してみます。
$ rain build -p "simple VPC"
AWSTemplateFormatVersion: '2010-09-09'
Description: Simple VPC CloudFormation template
Resources:
MyVpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: MyVPC
特に細かい指示はしていませんが、テンプレートを出力してくれました。
バックグラウンドでは与えたプロンプトを使ってrainがBedrockのClaudeにリクエストを投げているそうです。
出力はデフォルトでは標準出力です。ファイル出力したい場合は、以下のようにリダイレクトを使います。(ドキュメントにはオプション-o
で出力先指定と書いてったのですが、できませんでした)
$ rain build -p "simple VPC" > template1.yaml
AI生成出力の確認
少し指示の難易度をあげて、出力される内容を確認します。
以下のプロンプトを与えてみました。
$ rain build -p \
> "VPC with 2 public subnets and 2 private subnets.
> Every public subnet have NAT gateway. Public subnet with route table that route to Internet gateway.
> Private subnet with route table that route to NAT gateway.
> One EC2 t2.micro instance of Ubuntu 22.04 and output SSH Key Info.
> " > template2.yaml
完全な出力は長くなるので、記事の最後に記載しています。全部見たい方は参照ください。
ここでは、気になった部分だけ切り取って出力を確認します。
AIによる作成なので再現性があるかは微妙ですが、こんなこともあったというサンプル程度に見て頂ければと思います。
AIのメッセージが余計に出力されている
まずはじめに、テンプレートの先頭行にLLMの出力っぽい行がありました。
期待としてはCfnテンプレートの内容だけを出力してほしいのですが、こういったところは制御しきれていないようですね。
Here is the AWS CloudFormation YAML template for the requested infrastructure:
AWSTemplateFormatVersion: '2010-09-09'
論理参照はOK
以下はVPC Endpointの定義部分です。
VPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
RouteTableIds:
- !Ref PublicRouteTable
- !Ref PrivateRouteTable1
- !Ref PrivateRouteTable2
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .s3
VpcId: !Ref VPC
RouteTablesIdsの参照では!Ref
を使って他のリソースを参照できています。
ServiceNameの部分でもリージョンをスタック実行リージョンのものと出来るように事前定義パラメータAWS::Region
を利用できています。
!Sub "con.amazonaws.${AWS::Region}.s3"
とした方が可読性は高いですが、このくらいはご愛嬌と思えます。
キー情報をハードコード
KeyPairの定義部分です。
EC2KeyPair:
Type: AWS::EC2::KeyPair
Properties:
KeyName: myKey
PublicKeyMaterial: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com
KeyType: rsa
自前で生成したキーをハードコードして記入するという斬新な方法をとっています。ここは値を指定せずAWSにキーペアを生成してもらうほうが主流だと思います。AIが考えだしたキーのためPrivateKeyを取得する方法はないです。
こういったことが起こりえるので、注意が必要そうです。
EC2のAMI IDは実在の物
EC2インスタンスの定義です。
EC2Instance:
Type: AWS::EC2::Instance
Properties:
KeyName: !Ref KeyName
InstanceType: t2.micro
ImageId: ami-0557a15b87f6559cf
SubnetId: !Ref PublicSubnet1
SecurityGroupIds:
- !Ref InstanceSecurityGroup
ImageIdがami-0557a15b87f6559cf
と指定されていて、これも生成AIが創造した値かと疑って調べたところ、実在のUbuntuイメージでした。
必ずそうなるという訳ではありませんが、今回のUbuntu AMI IDのように多用される固有値は出力が可能化もしれません。
AI生成出力の確認 日本語
Rainのツール自体は英語なのですが、テンプレート作成の部分はBedrockの基盤モデルを利用しているため、日本語でもある程度は出力してくれると見込んで日本語も試してみました。内容は先ほどの例と同じです。
$ rain build -p \
> "2つのパブリックサブネットと2つのプライベートサブネットを持つVPC。
VPCにはインターネットゲートウェイ、S3用VPCエンドポイントがあります。
各パブリックサブネットにはNATゲートウェイがあります。パブリックサブネットにはインターネットゲートウェイへのルートテーブルがあります。プライベートサブネットにはNATゲートウェイへのルートテーブルがある。
Ubuntu 22.04のEC2 t2.microインスタンス1台とSSHキー情報。
> " > template3.yaml
こちらも結果全量は記事最後に記載しています。
結果としては、英語のプロンプトで生成したものとあまり差はありませんでした。使い込んでいくと差は出てくるかもしれませんが、日本語でもある程度使えそうな印象でした。
diff機能
他の機能として、rainにはdiff機能があります。
以下のようなコマンドで差分を表示することができます。
$ rain diff {template1} {template2}
一般的な文字列のdiffだと、リソースの記述位置を移動したときに出力が多くなってしまいがちですが、こちらを使うとYAMLの構成的に差分があった部分だけ表示してくれているように見えます。
またリソースがまるごと削除された場合などはリソース名だけ表示するなど、出力は比較的見やすいです。
forecast機能
実験的実装の段階ですがrain forecast
というコマンドを使うと、テンプレートのデプロイ時にエラーが発生しそうな箇所を事前に検証してくれるようです。
先ほどの出力テンプレートに対して実行してみた結果が以下の通りです。
$ rain forecast --experimental template3.yaml
Stormy weather ahead! 🌪
16 checks failed out of 42 total checks
7: AWS::EC2::VPC VPC - Insufficient permissions to create arn:aws:ec2:us-east-1:000000000000:vpc/rain-3609dc39-85fd-49f8-9dab-4af1c953ee3a
ec2:DescribeVpcs not allowed on arn:aws:ec2:us-east-1:000000000000:vpc/rain-3609dc39-85fd-49f8-9dab-4af1c953ee3a
14: AWS::EC2::InternetGateway InternetGateway - Insufficient permissions to create arn:aws:ec2:us-east-1:000000000000:internet-gateway/rain-d0420f95-3c31-45d4-ac09-2b5f4a8b4517
ec2:DescribeInternetGateways not allowed on arn:aws:ec2:us-east-1:000000000000:internet-gateway/rain-d0420f95-3c31-45d4-ac09-2b5f4a8b4517
~略~
42個のチェックが走り、16がfailedとなっています。実際のエラー内容を見ると16個が全て「作成のパーミッションが無い」というエラーでした。実行ユーザにはその権限はあるのでこのエラー自体は誤りです。
結果としてはエラーのバリエーションも精度もまだ有用なものではなさそうですが、今後に期待したいです。
おわりに
CloudFormationのテンプレートを作る時、多くの人がLLMを使いながら作成している時代だとは思います。筆者もLLMにサポートを受けながらCfnテンプレートを作っていますが、もっと良いLLMの使い方があるんじゃないかと思いながら悶々としていたので、AWSのリポジトリにあったツールが気になり今回は動作確認をしてみました。
現段階では、自身でClaudeにCfnテンプレを書かせるのと大きな差はなさそうだったのですが、Rainはまだ改良段階ではあると思うので今後の改良も見守っていきたいと思います。
(補足)出力サンプル
rain build 例1
入力
simple VPC
出力
出力
AWSTemplateFormatVersion: '2010-09-09'
Description: Simple VPC CloudFormation template
Resources:
MyVpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: MyVPC
rain build 例2
入力
VPC with 2 public subnets and 2 private subnets.
VPC has Internet gateway, VPC Endpoint for S3.
.Every public subnet have NAT gateway. Public subnet with route table that route to Internet gateway. Private subnet with route table that route to NAT gateway.
One EC2 t2.micro instance of Ubuntu 22.04 and output SSH Key Info.
出力
出力
Here is the AWS CloudFormation YAML template for the requested infrastructure:
AWSTemplateFormatVersion: '2010-09-09'
Description: CloudFormation template to deploy VPC with public and private subnets, NAT Gateway, Internet Gateway, VPC Endpoint, EC2 instance
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
InternetGateway:
Type: AWS::EC2::InternetGateway
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: 10.0.1.0/24
MapPublicIpOnLaunch: true
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 1, !GetAZs '' ]
CidrBlock: 10.0.2.0/24
MapPublicIpOnLaunch: true
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: 10.0.3.0/24
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 1, !GetAZs '' ]
CidrBlock: 10.0.4.0/24
NatGateway1:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NATIP1.AllocationId
SubnetId: !Ref PublicSubnet1
NatGateway2:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NATIP2.AllocationId
SubnetId: !Ref PublicSubnet2
NATIP1:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NATIP2:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PublicRoute:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref PublicRouteTable
PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet2
RouteTableId: !Ref PublicRouteTable
PrivateRouteTable1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PrivateRoute1:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable1
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway1
PrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable1
SubnetId: !Ref PrivateSubnet1
PrivateRouteTable2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PrivateRoute2:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable2
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway2
PrivateSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable2
SubnetId: !Ref PrivateSubnet2
VPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
RouteTableIds:
- !Ref PublicRouteTable
- !Ref PrivateRouteTable1
- !Ref PrivateRouteTable2
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .s3
VpcId: !Ref VPC
EC2Instance:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-0557a15b87f6559cf
KeyName: !Ref EC2KeyPair
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: "0"
GroupSet:
- !Ref InstanceSecurityGroup
SubnetId: !Ref PublicSubnet1
EC2KeyPair:
Type: AWS::EC2::KeyPair
Properties:
KeyName: myKey
PublicKeyMaterial: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com
KeyType: rsa
InstanceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow SSH traffic
VpcId: !Ref VPC
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
FromPort: 22
ToPort: 22
IpProtocol: tcp
Outputs:
EC2InstanceId:
Value: !Ref EC2Instance
Description: EC2 Instance Id
EC2InstancePublicIp:
Value: !GetAtt EC2Instance.PublicIp
Description: EC2 Instance Public IP Address
EC2InstancePublicDNS:
Value: !GetAtt EC2Instance.PublicDnsName
Description: EC2 Instance Public DNS Name
SSHKeyName:
Value: !Ref EC2KeyPair
Description: SSH key name to login into EC2 instance
rain build 例3
入力
2つのパブリックサブネットと2つのプライベートサブネットを持つVPC。
VPCにはインターネットゲートウェイ、S3用VPCエンドポイントがあります。
各パブリックサブネットにはNATゲートウェイがあります。パブリックサブネットにはインターネットゲートウェイへのルートテーブルがあります。プライベートサブネットにはNATゲートウェイへのルートテーブルがある。
Ubuntu 22.04のEC2 t2.microインスタンス1台とSSHキー情報。
出力
出力
Here is the AWS CloudFormation YAML template for the requested infrastructure:
AWSTemplateFormatVersion: '2010-09-09'
Description: VPC infrastructure with public and private subnets
Parameters:
KeyName:
Description: Name of an existing EC2 key pair
Type: AWS::EC2::KeyPair::KeyName
ConstraintDescription: Must be the name of an existing EC2 key pair.
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
InternetGateway:
Type: AWS::EC2::InternetGateway
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: 10.0.1.0/24
MapPublicIpOnLaunch: true
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 1, !GetAZs '' ]
CidrBlock: 10.0.2.0/24
MapPublicIpOnLaunch: true
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: 10.0.3.0/24
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 1, !GetAZs '' ]
CidrBlock: 10.0.4.0/24
NatGateway1EIP:
Type: AWS::EC2::EIP
DependsOn: InternetGatewayAttachment
Properties:
Domain: vpc
NatGateway2EIP:
Type: AWS::EC2::EIP
DependsOn: InternetGatewayAttachment
Properties:
Domain: vpc
NatGateway1:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGateway1EIP.AllocationId
SubnetId: !Ref PublicSubnet1
NatGateway2:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGateway2EIP.AllocationId
SubnetId: !Ref PublicSubnet2
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
DefaultPublicRoute:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet1
PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet2
PrivateRouteTable1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PrivateRoute1:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable1
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway1
PrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable1
SubnetId: !Ref PrivateSubnet1
PrivateRouteTable2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PrivateRoute2:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable2
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway2
PrivateSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable2
SubnetId: !Ref PrivateSubnet2
S3Endpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
RouteTableIds:
- !Ref PublicRouteTable
- !Ref PrivateRouteTable1
- !Ref PrivateRouteTable2
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .s3
VpcId: !Ref VPC
EC2Instance:
Type: AWS::EC2::Instance
Properties:
KeyName: !Ref KeyName
InstanceType: t2.micro
ImageId: ami-0557a15b87f6559cf
SubnetId: !Ref PublicSubnet1
SecurityGroupIds:
- !Ref InstanceSecurityGroup
InstanceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Enable SSH access via port 22
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
FromPort: 22
ToPort: 22
IpProtocol: tcp
VpcId: !Ref VPC
Outputs:
VPCId:
Description: The ID of the VPC
Value: !Ref VPC
Export:
Name: !Join [ ":", [ !Ref "AWS::StackName", VPCID ] ]
PublicSubnets:
Description: A list of the public subnets
Value: !Join [ ",", [ !Ref PublicSubnet1, !Ref PublicSubnet2 ]]
Export:
Name: !Join [ ":", [ !Ref "AWS::StackName", PublicSubnets ] ]
PrivateSubnets:
Description: A list of the private subnets
Value: !Join [ ",", [ !Ref PrivateSubnet1, !Ref PrivateSubnet2 ]]
Export:
Name: !Join [ ":", [ !Ref "AWS::StackName", PrivateSubnets ] ]
EC2InstanceId:
Description: The ID of the EC2 instance
Value: !Ref EC2Instance
Export:
Name: !Join [ ":", [ !Ref "AWS::StackName", EC2InstanceID ] ]