Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can we use serverless.yml to create an AWS S3 bucket and add a file to it?

I'm wondering if it's possible to leverage serverless.yml to create a bucket and add a specific file to it during the deploy process of serverless-framework.

So far, I've been able to add the S3 resource that creates the bucket, but not sure how to add a specific file.

resources:
  Resources:
    UploadBucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: ${self:custom.s3.bucket}
        AccessControl: Private
        CorsConfiguration:
          CorsRules:
          - AllowedMethods:
            - GET
            - PUT
            - POST
            - HEAD
            AllowedOrigins:
            - "*"
            AllowedHeaders:
            - "*"

Not sure if it's possible, or how to leverage the serverless.yml to upload a default file during the deploy process if it's not there yet.

like image 805
Pedro Baptista Afonso Avatar asked Jan 10 '17 15:01

Pedro Baptista Afonso


People also ask

Can we create a file in S3 bucket?

You simply need an item in your S3 bucket that you can move for the purposes of creating a new folder. Select the dummy file (check the box) and select Move from the dropdown menu and click the Apply button. In the destination path, specify the folder name (e.g. newfolder) that you would like to create.


3 Answers

There is no official AWS CloudFormation resource that will manage (add/delete) an individual S3 Object within a Bucket, but you can create one with a Custom Resource that uses a Lambda function to call the PUT Object/DELETE Object APIs using the AWS SDK for NodeJS.

Here's a complete example CloudFormation template:

Launch Stack

Description: Create an S3 Object using a Custom Resource.
Parameters:
  BucketName:
    Description: S3 Bucket Name (must not already exist)
    Type: String
  Key:
    Description: S3 Object Key
    Type: String
  Body:
    Description: S3 Object Body
    Type: String
Resources:
  Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref BucketName
  S3Object:
    Type: Custom::S3Object
    Properties:
      ServiceToken: !GetAtt S3ObjectFunction.Arn
      Bucket: !Ref Bucket
      Key: !Ref Key
      Body: !Ref Body
  S3ObjectFunction:
    Type: AWS::Lambda::Function
    Properties:
      Description: S3 Object Custom Resource
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          var response = require('cfn-response');
          var AWS = require('aws-sdk');
          var s3 = new AWS.S3();
          exports.handler = function(event, context) {
            var respond = (e) => response.send(event, context, e ? response.FAILED : response.SUCCESS, e ? e : {});
            var params = event.ResourceProperties;
            delete params.ServiceToken;
            if (event.RequestType == 'Create' || event.RequestType == 'Update') {
              s3.putObject(params).promise()
                .then((data)=>respond())
                .catch((e)=>respond(e));
            } else if (event.RequestType == 'Delete') {
              delete params.Body;
              s3.deleteObject(params).promise()
                .then((data)=>respond())
                .catch((e)=>respond(e));
            } else {
              respond({Error: 'Invalid request type'});
            }
          };
      Timeout: 30
      Runtime: nodejs4.3
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal: {Service: [lambda.amazonaws.com]}
          Action: ['sts:AssumeRole']
      Path: /
      ManagedPolicyArns:
      - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
      Policies:
      - PolicyName: S3Policy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - 's3:PutObject'
                - 'S3:DeleteObject'
              Resource: !Sub "arn:aws:s3:::${BucketName}/${Key}"

You should be able to also use these resources within a serverless.yml configuration file, though I'm not positive about how exactly Serverless integrates with CloudFormation resources/parameters.

like image 141
wjordan Avatar answered Oct 08 '22 17:10

wjordan


If you're doing this to deploy a website, you can use serverless-finch and it will create the bucket for you automatically if it doesn't already exist.

like image 33
V Maharajh Avatar answered Oct 08 '22 18:10

V Maharajh


Have you looked into the serverless-s3-sync plugin. It allows you to upload a folder to S3 from your local machine as part of the deployment.

plugins:
  - serverless-s3-sync
custom:
  s3Sync:
    - bucketName: ${self:custom.s3.bucket} # required
      bucketPrefix: assets/ # optional
      localDir: dist/assets # required
like image 4
Sam Williams Avatar answered Oct 08 '22 16:10

Sam Williams