我们有一个CloudFormation模板,用于创建EC2实例和安全组(以及许多其他资源),但我们需要能够将一些其他预先存在的安全组添加到同一个EC2实例。
我们遇到的问题是预先存在的安全组的数量并不总是相同,我们希望有一个模板可以处理所有情况。
目前,我们有一个输入参数,如下所示:
"WebTierSgAdditional": {
"Type": "String",
"Default": "",
"Description": ""
}
我们将此参数传递给逗号分隔的预先存在的安全组字符串,例如'sg-abc123,sg-abc456'
。
EC2实例的SecurityGroup标记如下所示:
"SecurityGroups": [
{
"Ref": "WebSg"
},
{
"Ref": "WebTierSgAdditional"
}
]
使用此代码,在创建实例时,我们会在AWS控制台中收到此错误:
必须对所有安全组使用group-id或group-name,而不是同时使用两者
上面的'WebSg'参考是在模板中其他地方创建的安全组之一。如果我们通过输入参数传入组名列表而不是组ID列表,则会出现同样的错误。
当我们将输入参数的“类型”字段更改为“CommaDelimitedList”时,我们收到错误消息:
属性值SecurityGroups的类型为List of String
显然无法使用字符串连接列表以使其成为新列表。
当参数只包含一个sg id时,所有内容都会成功创建,但是,我们需要能够添加多个sg id。
我们尝试过在SecurityGroups标记中使用Fn::Join
的许多不同组合,但似乎没有任何效果。我们真正需要的是某种“爆炸”功能,用于从参数字符串中提取单个id。
有谁知道一个很好的方法让它工作?
答案 0 :(得分:10)
正如您所知,问题是您需要将字符串列表作为安全组发送,尽管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" }
}
}
}
}
我很想知道是否有更好的方法来做到这一点。如你所说,它确实需要一个爆炸功能。
答案 1 :(得分:7)
AWS在Fn::Split中引入了January 2017,现在可以实现。它并不漂亮,但您实际上是将两个列表转换为Fn::Join
的字符串,然后将字符串转换回Fn::Split
的列表。
Parameters:
WebTierSgAdditional:
Type: CommaDelimitedList
Default: ''
Description: ''
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
将采用字符串并将项目拆分为以逗号分隔的列表。
答案 2 :(得分:3)
我从支持中获得了另一种解决方案,我发现更好。
基本上它只是从列表中获取每个安全组的索引,然后在最后添加内部安全组。
"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"
}
],
希望这有助于其他任何人。
答案 3 :(得分:1)
你实际上可以在lambda中编写必要的转换函数而不创建子包,以获得必要的List-String。
实际上,除了接收列表之外,此功能并不需要做任何其他事情并将其返回。我有一个类似的场景,我有一个List-AWS :: EC2 :: Subnet :: Id-并且需要将它传递给需要List-String的资源。
过程:
定义LambdaExecutionRole。
定义类型为AWS :: Lambda :: Function的ListToStringListFunction,添加在cfnresponse数据中返回的代码{'结果':event [' ResourceProperties'] ['列表'。]}
定义自定义资源MyList类型Custom :: MyList,为GetAtt的ServiceToken提供ListToStringListFunction Arn。也将List作为引用原始列表的属性传递。
使用Fn :: GetAtt(MyList,Result)引用MyList
答案 4 :(得分:1)
更简单的2线解决方案如下:
如果你在映射中有一个如下所示的公共AppSecurityGroups
,或者可以作为参数:
"AppSecurityGroups" : "sg-xxx,sg-yyyy,sg-zzz"
和第二个安全组(特定于服务)ServiceSecurityGroup
,其值为sg-aaa
,作为CFT中的参数。
在服务安全组上写一个条件,检查它是否为无。
"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资源使用的安全组列表中。
我已经测试并将其用于类似的问题,效果非常好。
答案 5 :(得分:0)
按照@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"]
}
}
]
}
]
}
}