问题描述
我想根据其中一个 yaml 文件中提到的数据,通过 CI/CD 管道自动执行存储桶创建过程。所以,我得到了包含所有存储桶名称的 bucket.yaml 文件。这个文件不断变化,因为将来会添加更多的存储桶名称。目前,bucket.yaml 是这样的
BucketName:
- test-bucket
- test-bucket2
- test-bucket3
我有一个 template.yaml 文件,它是一个用于创建 s3 存储桶的 cloudformation 模板。这是它的外观:
Resources:
S3Bucket:
Type: 'AWS::S3::Bucket'
DeletionPolicy: Retain
Properties:
BucketName: This will come from bucket.yaml
现在,template.yaml 将从bucket.yaml 文件中获取bucket 名称,并且应该创建bucket.yaml 中提到的3 个bucket。如果有人在 bucket.yaml 中再添加 2 个桶,那么 template.yaml 也应该创建这 2 个新桶。此外,如果有人从 bucket.yaml 中删除了任何存储桶名称,那么这些存储桶也应该被删除。我在我的研究中找不到过程,只是找到了点点滴滴的信息。所以,在这里我有一些具体的问题,如果可能的话:
- 如何从bucket.yaml 和template.yaml 中获取bucket 名称应该创建所有bucket。
- 如果有人在 bucket.yaml 中更新/添加/删除存储桶名称,template.yaml 应相应地更新这些名称。 另外,请解释我将如何通过 Azure DevOps 中的 CI/CD 管道来实现。
解决方法
- 关于您的第一个问题:
如何从 bucket.yaml 和 template.yaml 中获取桶名应该创建所有的桶。
在 bucket.yaml
中,您可以使用 Parameters 来设置 BucketName
。
例如:
parameters:
- name: BucketName
type: object
default:
- test-bucket
- test-bucket2
- test-bucket3
steps:
- ${{ each value in parameters.BucketName }}:
- script: echo ${{ value }}
此处的步骤可以遍历参数 BucketName
的值。
在 template.yaml
中,您可以像下面这样调用 bucket.yaml
。
trigger:
- main
extends:
template: bucket.yaml
- 对于您的第二个问题:
如果有人在 bucket.yaml 中更新/添加/删除存储桶名称,template.yaml 应相应地更新这些名称。
没有任何简单的方法可以做到这一点。可以尝试写一个脚本在pipeline中运行来做以下事情:
- 列出所有已创建的存储桶。这是现有存储分区的列表。
- 将现有存储桶列表与参数
BucketName
的值列表进行比较,以检查哪些存储桶需要添加,哪些需要删除。 - 如果某个存储桶在参数中列出但不在现有存储桶中,则应将此存储桶创建为新存储桶。
- 如果一个bucket在现有buckets中列出,但没有在参数中,这个bucket应该被删除。
BucketName:
- test-bucket
- test-bucket2
- test-bucket3
要求意味着所有 S3 存储桶都将以相同的方式创建,并且不需要偏离给定的 Cloudformation 模板 (AWS::S3::Bucket
)。
要求我们跟踪需要删除哪些 S3 存储桶。 Cloudformation 不会删除 S3 存储桶,因为 Cloudformation 模板片段包含 DeletionPolicy 的 Retain。
解决方案:
可以以特定方式标记 S3 存储桶,以将它们标识为由当前 CI/CD 管道拥有。可以列出 S3 存储桶,然后可以删除所有以正确方式标记但在 bucket.yaml
中不存在的 S3 存储桶。
我个人只会使用 AWS 开发工具包创建 CI/CD 管道所需的 S3 存储桶,并手动管理 S3 存储桶的删除。如果应用程序需要 S3 存储桶,那么他们应该在其应用程序的 Cloudformation 堆栈中自行创建它,以便他们可以!Ref 并按照他们想要的方式对其进行自定义(例如静态加密、版本控制、生命周期规则等)。
技术说明:
要删除 S3 存储桶,还需要删除其内容。这将要求我们列出 S3 存储桶中的所有对象,然后删除它们。 Java SDK [here] 的一些文档。 之后,删除 S3 存储桶的 API 调用才会成功。
您可以让 Cloudformation 使用 custom resource 删除您的 S3 对象。也就是说,我觉得使用自定义资源并不有趣 - 因此,如果您可以在 CI/CD 管道中使用 AWS 开发工具包,我可能只会使用它。
用于删除存储桶内容的自定义资源在 Cloudformation 中可能如下所示:(它是启动 Lambda 的自定义资源。如果自定义资源被取消配置,Lambda 将删除 S3 存储桶内容)
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cfn-customresource.html
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/walkthrough-custom-resources-lambda-lookup-amiids.html
ExampleBucketOperationCustomResource:
Type: AWS::CloudFormation::CustomResource
DependsOn: [Bucket,ExampleBucketOperationLambdaFunction]
Properties:
ServiceToken: !GetAtt ExampleBucketOperationLambdaFunction.Arn
# Custom properties
BucketToUse: !Ref S3BucketName
ExampleBucketOperationLambdaFunctionExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: "ExampleBucketOperationLambda-ExecutionRole"
Path: "/"
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Principal:
Service:
- lambda.amazonaws.com
Policies:
- PolicyName: "ExampleBucketOperationLambda-CanAccessCloudwatchLogs"
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
- PolicyName: "ExampleBucketOperationLambda-S3BucketLevelPermissions"
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- s3:ListBucket
Resource:
- !Sub "arn:aws:s3:::${S3BucketName}"
- PolicyName: "ExampleBucketOperationLambda-S3ObjectLevelPermissions"
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- s3:DeleteObject
- s3:PutObject
Resource:
- !Sub "arn:aws:s3:::${S3BucketName}/*"
# Test payload:
# {"RequestType":"Create","ResourceProperties":{"BucketToUse":"your-bucket-name"}}
ExampleBucketOperationLambdaFunction:
Type: AWS::Lambda::Function
DependsOn: ExampleBucketOperationLambdaFunctionExecutionRole
# DeletionPolicy: Retain
Properties:
FunctionName: "ExampleBucketOperationLambda"
Role: !GetAtt ExampleBucketOperationLambdaFunctionExecutionRole.Arn
Runtime: python3.8
Handler: index.handler
Timeout: 30
Code:
ZipFile: |
import boto3
import cfnresponse
def handler(event,context):
eventType = event["RequestType"]
print("The event type is: " + str(eventType));
bucketToUse = event["ResourceProperties"]["BucketToUse"]
print("The bucket to use: " + str(bucketToUse));
try:
# Requires s3:ListBucket permission
if (eventType in ["Delete"]):
print("Deleting everyting in bucket: " + str(bucketToUse));
s3Client = boto3.client("s3")
s3Bucket = boto3.resource("s3").Bucket(bucketToUse)
for currFile in s3Bucket.objects.all():
print("Deleting file: " + currFile.key);
s3Client.delete_object(Bucket=bucketToUse,Key=currFile.key)
print("All done")
responseData = {}
cfnresponse.send(event,context,cfnresponse.SUCCESS,responseData)
except Exception as e:
responseData = {}
errorDetail = "Exception: " + str(e)
errorDetail = errorDetail + "\n\t More detail can be found in CloudWatch Log Stream: " + context.log_stream_name
print(errorDetail)
cfnresponse.send(event=event,context=context,responseStatus=cfnresponse.FAILED,responseData=responseData,reason=errorDetail)
,
感谢以上回答。我采取了不同的方法来解决这个问题。我使用 AWS CDK 来实现我真正想要的。我个人将 AWS CDK 用于 Python 并使用它创建了基础设施。