gpt4 book ai didi

amazon-web-services - 使用带有 CommaDelimitedList 参数的 Cloudformation Join 函数来构建 IAM ARN

转载 作者:行者123 更新时间:2023-12-04 17:29:40 25 4
gpt4 key购买 nike

我一直在尝试构建一个存储桶策略,以允许对 CloudFormation 中的集中帐户执行操作到共享相同模式的一系列其他帐户中的 IAM 角色 - 即:

arn:aws:iam::111111111111:role/my-role

arn:aws:iam::222222222222:role/my-role

我发现了以下示例,它让我很接近,但还不够接近: https://stackoverflow.com/a/50060983/1736704

下面是一个有效的代码示例:

Parameters:
MyAccounts:
Type: CommaDelimitedList
Default: '111111111111,222222222222'
MyBucket:
Type: String
Default: my-bucket

Resources:
MyBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref MyBucket
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: MyRoleAllow
Effect: Allow
Principal:
AWS: !Split
- ','
- !Sub
- 'arn:aws:iam::${inner}:role/my-role'
- inner: !Join
- ':role/my-role,arn:aws:iam::'
- Ref: 'MyAccounts'
Action:
- s3:PutObject
Resource: !Sub arn:aws:s3:::${MyBucket}/*

我希望能够做的是将角色名称作为参数。当我尝试这样做时,无论我如何构造 !Join 函数,我都会收到错误。

如果我修改上面的代码并将 my-role 作为名为 RoleName 的字符串参数,并展开 !Join,它会返回一个错误。完整修改后的代码不起作用:

Parameters:
MyAccounts:
Type: CommaDelimitedList
Default: '111111111111,222222222222'
RoleName
Type: String
Default: my-role
MyBucket:
Type: String
Default: my-bucket

Resources:
MyBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref MyBucket
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: MyRoleAllow
Effect: Allow
Principal:
AWS: !Split
- ','
- !Sub
- 'arn:aws:iam::${inner}:role/my-role'
- inner: !Join
- ''
- - ':role/'
- !Ref 'RoleName'
- ',arn:aws:iam::'
- Ref: 'Accounts'
Action:
- s3:PutObject
Resource: !Sub arn:aws:s3:::${MyBucket}/*

这是我收到的错误消息:模板错误:每个 Fn::Join 对象都需要两个参数,(1) 字符串分隔符和 (2) 要连接的字符串列表或返回字符串列表的函数(例如 Fn::GetAZs)待加入。

在修改后的代码中,是 Ref: 'Accounts' 导致了问题,但我很困惑为什么,因为它在原始代码中有效。

编辑:
我想使用的输入是:

Parameters:
MyAccounts:
Type: CommaDelimitedList
Default: '111111111111,222222222222'
RoleName
Type: String
Default: my-role
MyBucket:
Type: String
Default: my-bucket

我的预期输出(S3 存储桶策略)如下所示:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "MyRoleAllow",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::111111111111:role/my-role",
"arn:aws:iam::222222222222:role/my-role"
]
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::my-bucket/*"
}
]
}

谁能告诉我我想要实现的目标是否可能?如果是这样,我该如何修改我的代码以使其正常工作?

谢谢

最佳答案

假设我有:

Parameters:
ChildAccounts:
Type: CommaDelimitedList
Default: ""
Description: Comma delimited list of accounts.

如果你能巧妙地使用 !Join!Sub!Split 你实际上可以让它工作,如果你不这样做的话需要添加任何其他参数:

  - Effect: Allow
Action:
- sts:AssumeRole
Resource: !Split
- ','
- !Sub
- arn:aws:iam::${MIDDLE_ACOUNTs}:role/ChildAccountRole
- MIDDLE_ACOUNTs: !Join
- ':role/ChildAccountRole,arn:aws:iam::'
- !Ref ChildAccounts

但是,遗憾的是,在我的例子中,我需要将一个名为 Environment 的不同参数子到每个角色名称中,当我尝试这样做时,上述方法都失败了。

因此,经过足够多的头脑碰撞后,我采用了不同的方法,并决定使用 Cloudformation Macros / Transformations 添加自定义内部函数。 .

首先我需要部署一个宏。该宏将在模板资源中查找名为 Tr::ListSub 的键,其值为两/三元素列表(稍后在示例中进一步解释)。

下面的CloudFormation将创建我们可以使用的宏。您应该先部署此模板,然后再在其他模板中使用创建的宏。

Resources:

MacroLambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: LoggingPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: '*'

MacroLambda:
Type: 'AWS::Lambda::Function'
Properties:
Handler: index.handler
Runtime: python3.8
Role: !GetAtt MacroLambdaRole.Arn
Code:
ZipFile: |
import json
import logging
from typing import Dict, Union, List
LOGGER = logging.getLogger()
LOGGER.setLevel(logging.INFO)
def render_sub_map(sub_map: Dict, template_param_value: Dict):
"""
ARGUMENTS:
`sub_map` example:
{
"Tr::ListSub": [
"${Environment}-${ListSubItem}-${AWS::Account}"
["VALUE1", "VALUE2", "VALUE3"],
{
"EXAMPLE": "test",
}
]
}
RETURNS
[
{"Fn::Sub": ["${Environment}-VALUE1-${AWS::Account}", {"EXAMPLE": "test"}]},
{"Fn::Sub": ["${Environment}-VALUE2-${AWS::Account}", {"EXAMPLE": "test"}]},
{"Fn::Sub": ["${Environment}-VALUE3-${AWS::Account}", {"EXAMPLE": "test"}]}
]
"""
sub_str = sub_map["Tr::ListSub"][0]
sub_list = sub_map["Tr::ListSub"][1]
if isinstance(sub_list, dict):
sub_list = template_param_value[sub_list["Ref"]]
sub_dict = {}
if len(sub_map["Tr::ListSub"]) == 3:
sub_dict = sub_map["Tr::ListSub"][2]
results = []
for item in sub_list:
new_sub_str = sub_str.replace("${ListSubItem}", item)
results.append({
"Fn::Sub": [
new_sub_str,
sub_dict
]
})
return results

def iterate_resources(resources: Dict, template_param_value: Dict) -> None:
""" Distructive to `fragment`. """
def _recurse_search(_dict_or_list: Union[Dict, List]):

if isinstance(_dict_or_list, dict):
for key, val in _dict_or_list.items():
if isinstance(val, dict):
if "Tr::ListSub" in val:
_dict_or_list[key] = render_sub_map(val, template_param_value)
else:
_recurse_search(val)
else:
_recurse_search(val)
elif isinstance(_dict_or_list, list):
for item in _dict_or_list:
_recurse_search(item)
_recurse_search(resources)

def handler(event, context):
LOGGER.info(json.dumps(event, indent=2))
iterate_resources(event["fragment"]["Resources"], event["templateParameterValues"])
response = {
"status": "success",
"fragment": event["fragment"],
"requestId": event["requestId"]
}
LOGGER.info(json.dumps(response, indent=2))
return response

MacroExample:
Type: AWS::CloudFormation::Macro
Properties:
Name: ListSubTransform
Description: Macro to create do a sub expression for each item in a list.
FunctionName: !GetAtt MacroLambda.Arn

MacroLambdaLogStream:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${MacroLambda}
RetentionInDays: 3

那么让我们解释一下这个宏是如何工作的。您可以在下面看到一些示例。

添加的宏称为Tr::ListSub

  • 第一个参数是一个字符串,例如您为 !Sub/Fn::Sub 提供的字符串,但不带前缀 !Sub。该字符串可以包含您可以添加到普通 !Sub 中的任何变量,例如 ${AWS::Account};但是,要在迭代时引用列表中的每个项目,请使用 ${ListSubItem}
  • 第二个参数是要迭代的项目列表。在下面的示例中,我 !Ref 一个 CommaDelimitedList 参数。
  • 第三个可选参数是一个字典,它是键值对,将添加到生成的 !Sub 表达式中。

这个描述有点难以理解,所以这里有几个例子供引用。

假设我的 CloudFormation 有以下内容:

{
"Tr::ListSub": [
"${Environment}-${ListSubItem}-${AWS::Account}"
["VALUE1", "VALUE2", "VALUE3"],
{
"EXAMPLE": "test",
}
]
}

然后,该宏将用以下内容替换该内在函数:

[
{"Fn::Sub": ["${Environment}-VALUE1-${AWS::Account}", {"EXAMPLE": "test"}]},
{"Fn::Sub": ["${Environment}-VALUE2-${AWS::Account}", {"EXAMPLE": "test"}]},
{"Fn::Sub": ["${Environment}-VALUE3-${AWS::Account}", {"EXAMPLE": "test"}]}
]

完整的 Cloudformation 模板示例:

Parameters:
Environment:
Type: String
ChildAccounts:
Type: CommaDelimitedList
Default: ""
Description: Comma delimited list of accounts.
Transform:
- ListSubTransform
Resources:
LambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: !Sub ${AWS::StackName}-STS-Policy
PolicyDocument:
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource:
Tr::ListSub:
- arn:aws:iam::${ListSubItem}:role/${Environment}-MetricMolePup
- !Ref ChildAccounts

此宏不是防弹的,但可以完成工作,除非您尝试将其嵌套在内部函数中,在这种情况下,AWS 将在调用转换之前出错。

关于amazon-web-services - 使用带有 CommaDelimitedList 参数的 Cloudformation Join 函数来构建 IAM ARN,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60925850/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com