EC2インスタンスとセキュリティグループ(他の多くのリソースの中で)を作成するCloudFormationテンプレートがありますが、同じEC2インスタンスにいくつかの既存のセキュリティグループを追加できる必要があります。
私たちが抱えている問題は、既存のセキュリティグループの数が常に同じであるとは限らないことであり、すべてのケースを処理する単一のテンプレートが必要です。
現在、次のような入力パラメータがあります。
"WebTierSgAdditional": {
"Type": "String",
"Default": "",
"Description": ""
}
このパラメータに、'sg-abc123,sg-abc456'
などの既存のセキュリティグループのカンマ区切りの文字列を渡します。
EC2インスタンスのSecurityGroupタグは次のようになります。
"SecurityGroups": [
{
"Ref": "WebSg"
},
{
"Ref": "WebTierSgAdditional"
}
]
このコードでは、インスタンスが作成されると、AWSコンソールで次のエラーが発生します。
両方を同時に使用するのではなく、すべてのセキュリティグループにgroup-idまたはgroup-nameのいずれかを使用する必要があります
上記の「WebSg」参照は、テンプレートの他の場所で作成されているセキュリティグループの1つです。入力パラメータを介してグループIDのリストではなく、グループ名のリストを渡すと、これと同じエラーが発生します。
入力パラメータの「Type」フィールドを「CommaDelimitedList」に変更すると、次のようなエラーが発生します。
プロパティSecurityGroupsの値は、List ofStringタイプである必要があります
明らかに、リストを文字列で結合して新しいリストにすることはできません。
パラメータに含まれるsgIDが1つだけの場合、すべてが正常に作成されますが、複数のsgidを追加する機能が必要です。
SecurityGroupsタグ内でFn::Join
を使用するさまざまな組み合わせを試しましたが、何も機能しないようです。本当に必要なのは、パラメータ文字列から個々のIDを抽出するためのある種の「Explode」関数です。
これを機能させるための良い方法を知っている人はいますか?
ご存知のように、問題は文字列のリストをセキュリティグループとして送信する必要があることです。CloudFormationは文字列のリストを単一の区切り文字列に結合する方法を提供しますが、簡単な方法は提供しません。区切られた文字列を文字列のリストに分割するため。
残念ながら、これを行う方法を私が知っている唯一の方法は、ネストされたスタックを使用することです。 パラメータタイプ「CommaDelimitedList」を使用して、カンマ区切りの文字列を文字列のリストに分割できます。
基本的な方法は次のとおりです。
次の2つのテンプレートを使用してこれをテストしましたが、機能しています。
メインテンプレート:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters" : {
"SecurityGroups" : {
"Description" : "A comma separated list of security groups to merge with the web security group",
"Type" : "String"
}
},
"Resources" : {
"WebSg" : ... your web security group here,
"Ec2Instance" : {
"Type" : "AWS::CloudFormation::Stack",
"Properties" : {
"TemplateURL" : "s3 link to the other stack template",
"Parameters" : {
"SecurityGroups" : { "Fn::Join" : [ ",", [ { "Ref" : "WebSg" }, { "Ref" : "SecurityGroups" } ] ] },
}
}
}
}
}
ネストされたテンプレート(上記の「TemplateURL」によってリンクされています):
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters" : {
"SecurityGroups" : {
"Description" : "The Security Groups to launch the instance with",
"Type" : "CommaDelimitedList"
},
}
"Resources" : {
"Ec2Instance" : {
"Type" : "AWS::EC2::Instance",
"Properties" : {
... etc
"SecurityGroupIds" : { "Ref" : "SecurityGroups" }
}
}
}
}
これを行うためのより良い方法があるかどうか知りたいです。あなたが言うように、それは本当に爆発機能を必要とします。
AWSは Fn :: Split を 2017年1月 に導入しました。これが可能になりました。きれいではありませんが、基本的に2つのリストをFn::Join
で文字列に変換してから、文字列をFn::Split
でリストに変換し直します。
Parameters:
WebTierSgAdditional:
Type: CommaDelimitedList
Default: ''
Conditions:
HasWebTierSgAdditional: !Not [ !Equals [ '', !Select [ 0, !Ref WebTierSgAdditional ] ] ]
Resources:
WebSg:
Type: AWS::EC2::SecurityGroup
Properties:
# ...
Ec2Instance:
Type: AWS::EC2::Instance
Properties:
# ...
SecurityGroupIds: !Split
- ','
- !Join
- ','
- - !Ref WebSg
- !If [ HasWebTierSgAdditional, !Join [ ',', !Ref WebTierSgAdditional ], !Ref 'AWS::NoValue' ]
WebTierSgAdditional
はリストとして始まりますが、各項目が!Join [ ',', !Ref WebTierSgAdditional ]
を介してコンマで区切られた文字列に変換されます。これは、!Ref WebSg
を含む別のリストに含まれています。このリストも、各項目がコンマで区切られた文字列に変換されます。 !Split
は文字列を受け取り、アイテムをコンマで区切ってリストに分割します。
私がサポートから得た別の解決策があり、私はより良いと思いました。
基本的には、リストからインデックスによって各セキュリティグループを取得し、最後に内部グループを追加するだけです。
"SecurityGroupList": {
"Description": "List of existing security groups",
"Type": "CommaDelimitedList"
},
...
"InternalSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup"
},
...
"SecurityGroupIds": [
{
"Fn::Select": [
"0",
{
"Ref": "SecurityGroupList"
}
]
},
{
"Fn::Select": [
"1",
{
"Ref": "SecurityGroupList"
}
]
},
{
"Fn::Select": [
"2",
{
"Ref": "SecurityGroupList"
}
]
},
{
"Ref": "InternalSecurityGroup"
}
],
これが他の誰かに役立つことを願っています。
@alanthingのアプローチに従って、JSON形式でこのソリューションを思いつきました。私の場合はチェックしていないので、パラメータが空でないことを確認してください。また、テンプレートが不完全であり、関連する部分が表示されているだけであることに注意してください。
誰かに役立つ場合に備えて、ここに投稿してください。
"Parameters": {
"SecurityGroups": {
"Type": "List<AWS::EC2::SecurityGroup::Id>",
"Description": "List of security groups"
}
},
"Resources": {
"EC2Instance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"SecurityGroupIds": {
"Fn::Split": [
",",
{
"Fn::Sub": [
"${SGIdsByParam},${SGByLogicalId}",
{
"SGIdsByParam": {
"Fn::Join": [",", {
"Ref": "SecurityGroups"
}]
},
"SGByLogicalId": {
"Fn::GetAtt": ["InstanceSecurityGroup", "GroupId"]
}
}
]
}
]
}
}
はるかに単純な2行のソリューションは次のようになります。
マッピングに以下のような共通のAppSecurityGroups
がある場合、またはパラメーターとして使用できる場合:
"AppSecurityGroups" : "sg-xxx,sg-yyyy,sg-zzz"
2番目のセキュリティグループ(サービス固有)ServiceSecurityGroup
値sg-aaa
CFTのパラメーターとして。
サービスセキュリティグループに条件を記述して、それがNoneかどうかを確認します。
"Conditions": {
"IsEmptySSG": {
"Fn::Equals": [
{"Ref": ServiceSecurityGroup"},
"None"
]
}
}
次に、ServiceSecurityGroup
が空でないという条件に基づいてセキュリティグループをマージします。
"SecurityGroups" : {
"Fn::If" : [
"IsEmptySSG",
{"Fn::Split" : [ "," , {"Ref" : "AppSecurityGroups"} ]},
{"Fn::Split" : [ "," ,
{ "Fn::Join" : [ ",", [{"Ref" : "AppSecurityGroups"}, { "Ref" : "ServiceSecurityGroup" }]]}
]
}
]
},
これにより、セキュリティグループの2つのcsvリストがAWSリソースで使用される1つに動的に追加されます。
私はそれをテストして同様の問題に使用しましたが、本当にうまくいきました。
サブスタックを作成せずに、必要な変換関数をラムダで実際に記述して、必要なList-Stringを取得できます。
実際、この関数は、リストを受信して返す以外に何もする必要はありません。 List-AWS :: EC2 ::サブネット:: Id-があり、それをList-Stringを必要とするリソースに渡す必要がある同様のシナリオがありました。
処理する:
LambdaExecutionRoleを定義します。
AWS :: Lambda :: FunctionタイプのListToStringListFunctionを定義し、cfnresponseデータ{'Result':event ['ResourceProperties'] ['List']}を返すコードを追加します。
カスタムリソースのMyListタイプCustom :: MyListを定義し、GetAttのServiceTokenにListToStringListFunctionArnを指定します。また、元のリストを参照するプロパティとしてListを渡します。
Fn :: GetAtt(MyList、Result)を使用してMyListを参照する