繁体   English   中英

如何在 azure devops CI/CD 管道中动态创建 s3 存储桶

[英]How to create s3 buckets dynamically in azure devops CI/CD pipeline

我想根据 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 文件中获取存储桶名称,并应创建 3 个存储桶,如 bucket.yaml 中所述。 如果有人在 bucket.yaml 中再添加 2 个存储桶,则 template.yaml 也应该创建这 2 个新存储桶。 此外,如果有人从 bucket.yaml 中删除任何存储桶名称,那么这些存储桶也应该被删除。 我在研究中找不到过程,只是找到了零碎的信息。所以,在这里我有一些具体的问题,如果可以的话:

  1. 如何从 bucket.yaml 和 template.yaml 中获取存储桶名称应该创建所有存储桶。
  2. 如果有人在 bucket.yaml 中更新/添加/删除存储桶名称,则 template.yaml 应相应地更新这些名称。 另外,请解释我将如何通过 Azure DevOps 中的 CI/CD 管道进行操作。
  1. 关于你的第一个问题:

如何从 bucket.yaml 和 template.yaml 中获取存储桶名称应该创建所有存储桶。

bucket.yaml ,您可以使用参数来设置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
  1. 对于你的第二个问题:

如果有人在 bucket.yaml 中更新/添加/删除存储桶名称,则 template.yaml 应相应地更新这些名称。

没有任何简单的方法可以做到这一点。 您可以尝试编写一个脚本以在管道中运行以执行以下操作:

  • 列出所有已创建的存储桶。 这是现有存储桶的列表。
  • 将现有存储桶列表与参数BucketName的值列表进行比较,检查哪些存储桶需要添加,哪些存储桶需要删除。
  • 如果参数中列出了存储桶,但现有存储桶中没有,则应将此存储桶创建为新存储桶。
  • 如果某个存储桶在现有存储桶中但未在参数中列出,则应删除该存储桶。
BucketName:
                  - test-bucket
                  - test-bucket2
                  - test-bucket3

这些要求意味着所有 S3 存储桶都将以相同的方式创建,并且不需要偏离给定的 Cloudformation 模板 ( AWS::S3::Bucket )。

这些要求要求我们跟踪需要删除哪些 S3 存储桶。 Cloudformation 不会删除 S3 存储桶,因为 Cloudformation 模板片段包含保留的DeletionPolicy

解决方案:

可以以特定方式标记 S3 存储桶,以将它们标识为当前 CI/CD 管道所拥有。 可以列出 S3 存储桶,并以正确的方式标记所有 S3 存储桶,但在存储桶中不存在。然后可以删除bucket.yaml


我个人会使用 AWS SDK 创建 CI/CD 管道所需的 S3 存储桶并手动管理 S3 存储桶删除。 如果应用程序需要 S3 存储桶,那么他们应该在其应用程序的 Cloudformation 堆栈中自己创建它,以便他们可以!引用它并按照他们想要的方式对其进行自定义(例如 rest 的加密、版本控制、生命周期规则等)。


技术说明:

要删除 S3 存储桶,还需要删除其内容。 这将要求我们列出 S3 存储桶中的所有对象,然后将其删除。 Java SDK [这里]的一些文档。 只有随后调用 API 才能成功删除 S3 存储桶。

您可以让 Cloudformation 使用自定义资源删除您的 S3 对象。 也就是说,我没有找到有趣的自定义资源 - 所以如果您可以在 CI/CD 管道中使用 AWS SDK,我可能会使用它。

用于删除存储桶内容的自定义资源在 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 并使用它创建了基础设施。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM