Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AWS Deploying environment and create environments for dev and prod

Greeting all,

I'm looking for a way to deploy my application which contains:

  • API Gateway
  • DynamoDB
  • Lambda Functions
  • An S3 bucket

I looked at CloudFormation and CodeDeploy but I'm unsure how to proceed without EC2...

All the information I find is for EC2, I haven't found any information regarding deploying the app above...

The goal is to have a deployment script that deploys app to an environment automatically with technology from AWS. (Basically duplicating my environment)

Any help would greatly be appreciated.

EDIT: I need to be able to export from one AWS account then import onto another AWS account.

Cheers!

like image 694
Luc Laverdure Avatar asked Sep 15 '25 02:09

Luc Laverdure


2 Answers

In order to deploy your CloudFormation stack into a "different" environment, you have to parameterize your CloudFormation stack name and resource names. (You don't have to parameterize the AWS::Serverless::Function function in this example because CloudFormation automatically creates a function name if no function name is specified, but for most other resources it's necessary)

Example CloudFormation template cfn.yml using the Serverless Application Model (SAM):

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Deploys a simple AWS Lambda using different environments.

Parameters:
  Env:
    Type: String
    Description: The environment you're deploying to.

Resources:
  ServerlessFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs12.x
      CodeUri: ./
      Policies:
        - AWSLambdaBasicExecutionRole

  MyBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub 'my-bucket-name-${Env}'

You can add further resources like a DynamoDB table. The API Gateway is automatically created if you're using SAM and provide an Events section in your AWS::Serverless::Function resource. See also this SAM example code from the serverless-app-examples repository.

Example deploy.sh script:

#!/usr/bin/env bash

LAMBDA_BUCKET="Your-S3-Bucket-Name"
# change this ENV variable depending on the environment you want to deploy
ENV="prd"
STACK_NAME="aws-lambda-cf-environments-${ENV}"

# now package the CloudFormation template which automatically uploads the Lambda function artifacts to S3 -> generated a "packaged" CloudFormation template cfn.packaged.yml
aws cloudformation package --template-file cfn.yml --s3-bucket ${LAMBDA_BUCKET} --output-template-file cfn.packaged.yml

# ... and deploy the packaged CloudFormation template
aws cloudformation deploy --template-file cfn.packaged.yml --stack-name ${STACK_NAME} --capabilities CAPABILITY_IAM --parameter-overrides Env=${ENV}

See the full example code here. Just deploy the script using ./deploy.sh and change the ENV variable.

like image 148
s.hesse Avatar answered Sep 17 '25 18:09

s.hesse


Based JSON examples.

Lambda function AWS::Lambda::Function

  • This example creates a Lambda function and an IAM Role attached to it.

Language: NodeJs.


"LambdaRole": {
    "Type": "AWS::IAM::Role",
    "Properties": {
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": [
                            "lambda.amazonaws.com"
                        ]
                    },
                    "Action": [
                        "sts:AssumeRole"
                    ]
                }
            ]
        },
        "Policies": [
            {
                "PolicyName": "LambdaSnsNotification",
                "PolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Sid": "AllowSnsActions",
                            "Effect": "Allow",
                            "Action": [
                                "sns:Publish",
                                "sns:Subscribe",
                                "sns:Unsubscribe",
                                "sns:DeleteTopic",
                                "sns:CreateTopic"
                            ],
                            "Resource": "*"
                        }
                    ]
                }
            }
        ]
    }
},
"LambdaFunctionMessageSNSTopic": {
    "Type": "AWS::Lambda::Function",
    "Properties": {
        "Description": "Send message to a specific topic that will deliver MSG to a receiver.",
        "Handler": "index.handler",
        "MemorySize": 128,
        "Role": {
            "Fn::GetAtt": [
                "LambdaRole",
                "Arn"
            ]
        },
        "Runtime": "nodejs6.10",
        "Timeout": 60,
        "Environment": {
            "Variables": {
                "sns_topic_arn": ""
            }
        },
        "Code": {
            "ZipFile": {
                "Fn::Join": [
                    "\n",
                    [
                        "var AWS = require('aws-sdk');",                        
                        "};"
                    ]
                ]
            }
        }
    }
}

API Gateway AWS::ApiGateway::RestApi

  • This example creates Role, RestAPI, Usageplan, Keys and permission to execute lambda from a Request method.

"MSGGatewayRestApi": {
    "Type": "AWS::ApiGateway::RestApi",
    "Properties": {
        "Name": "MSG RestApi",
        "Description": "API used for sending MSG",
        "FailOnWarnings": true
    }
},
"MSGGatewayRestApiUsagePlan": {
    "Type": "AWS::ApiGateway::UsagePlan",
    "Properties": {
        "ApiStages": [
            {
                "ApiId": {
                    "Ref": "MSGGatewayRestApi"
                },
                "Stage": {
                    "Ref": "MSGGatewayRestApiStage"
                }
            }
        ],
        "Description": "Usage plan for stage v1",
        "Quota": {
            "Limit": 5000,
            "Period": "MONTH"
        },
        "Throttle": {
            "BurstLimit": 200,
            "RateLimit": 100
        },
        "UsagePlanName": "Usage_plan_for_stage_v1"
    }
},
"RestApiUsagePlanKey": {
    "Type": "AWS::ApiGateway::UsagePlanKey",
    "Properties": {
        "KeyId": {
            "Ref": "MSGApiKey"
        },
        "KeyType": "API_KEY",
        "UsagePlanId": {
            "Ref": "MSGGatewayRestApiUsagePlan"
        }
    }
},
"MSGApiKey": {
    "Type": "AWS::ApiGateway::ApiKey",
    "Properties": {
        "Name": "MSGApiKey",
        "Description": "CloudFormation API Key v1",
        "Enabled": "true",
        "StageKeys": [
            {
                "RestApiId": {
                    "Ref": "MSGGatewayRestApi"
                },
                "StageName": {
                    "Ref": "MSGGatewayRestApiStage"
                }
            }
        ]
    }
},
"MSGGatewayRestApiStage": {
    "DependsOn": [
        "ApiGatewayAccount"
    ],
    "Type": "AWS::ApiGateway::Stage",
    "Properties": {
        "DeploymentId": {
            "Ref": "RestAPIDeployment"
        },
        "MethodSettings": [
            {
                "DataTraceEnabled": true,
                "HttpMethod": "*",
                "LoggingLevel": "INFO",
                "ResourcePath": "/*"
            }
        ],
        "RestApiId": {
            "Ref": "MSGGatewayRestApi"
        },
        "StageName": "v1"
    }
},
"ApiGatewayCloudWatchLogsRole": {
    "Type": "AWS::IAM::Role",
    "Properties": {
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": [
                            "apigateway.amazonaws.com"
                        ]
                    },
                    "Action": [
                        "sts:AssumeRole"
                    ]
                }
            ]
        },
        "Policies": [
            {
                "PolicyName": "ApiGatewayLogsPolicy",
                "PolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Action": [
                                "logs:CreateLogGroup",
                                "logs:CreateLogStream",
                                "logs:DescribeLogGroups",
                                "logs:DescribeLogStreams",
                                "logs:PutLogEvents",
                                "logs:GetLogEvents",
                                "logs:FilterLogEvents"
                            ],
                            "Resource": "*"
                        }
                    ]
                }
            }
        ]
    }
},
"ApiGatewayAccount": {
    "Type": "AWS::ApiGateway::Account",
    "Properties": {
        "CloudWatchRoleArn": {
            "Fn::GetAtt": [
                "ApiGatewayCloudWatchLogsRole",
                "Arn"
            ]
        }
    }
},
"RestAPIDeployment": {
    "Type": "AWS::ApiGateway::Deployment",
    "DependsOn": [
        "MSGGatewayRequest"
    ],
    "Properties": {
        "RestApiId": {
            "Ref": "MSGGatewayRestApi"
        },
        "StageName": "DummyStage"
    }
},
"ApiGatewayMSGResource": {
    "Type": "AWS::ApiGateway::Resource",
    "Properties": {
        "RestApiId": {
            "Ref": "MSGGatewayRestApi"
        },
        "ParentId": {
            "Fn::GetAtt": [
                "MSGGatewayRestApi",
                "RootResourceId"
            ]
        },
        "PathPart": "delivermessage"
    }
},
"MSGGatewayRequest": {
    "DependsOn": "LambdaPermission",
    "Type": "AWS::ApiGateway::Method",
    "Properties": {
        "ApiKeyRequired": true,
        "AuthorizationType": "NONE",
        "HttpMethod": "POST",
        "Integration": {
            "Type": "AWS",
            "IntegrationHttpMethod": "POST",
            "Uri": {
                "Fn::Join": [
                    "",
                    [
                        "arn:aws:apigateway:",
                        {
                            "Ref": "AWS::Region"
                        },
                        ":lambda:path/2015-03-31/functions/",
                        {
                            "Fn::GetAtt": [
                                "LambdaFunctionMessageSNSTopic",
                                "Arn"
                            ]
                        },
                        "/invocations"
                    ]
                ]
            },
            "IntegrationResponses": [
                {
                    "StatusCode": 200
                },
                {
                    "SelectionPattern": "500.*",
                    "StatusCode": 500
                },
                {
                    "SelectionPattern": "412.*",
                    "StatusCode": 412
                }
            ],
            "RequestTemplates": {
                "application/json": ""
            }
        },
        "RequestParameters": {
        },
        "ResourceId": {
            "Ref": "ApiGatewayMSGResource"
        },
        "RestApiId": {
            "Ref": "MSGGatewayRestApi"
        },
        "MethodResponses": [
            {
                "StatusCode": 200
            },
            {
                "StatusCode": 500
            },
            {
                "StatusCode": 412
            }
        ]
    }
},
"LambdaPermission": {
    "Type": "AWS::Lambda::Permission",
    "Properties": {
        "Action": "lambda:invokeFunction",
        "FunctionName": {
            "Fn::GetAtt": [
                "LambdaFunctionMessageSNSTopic",
                "Arn"
            ]
        },
        "Principal": "apigateway.amazonaws.com",
        "SourceArn": {
            "Fn::Join": [
                "",
                [
                    "arn:aws:execute-api:",
                    {
                        "Ref": "AWS::Region"
                    },
                    ":",
                    {
                        "Ref": "AWS::AccountId"
                    },
                    ":",
                    {
                        "Ref": "MSGGatewayRestApi"
                    },
                    "/*"
                ]
            ]
        }
    }
}

DynamoDB AWS::DynamoDB::Table

  • This example creates a DynamoDB table MyCrossConfig and an alarms for it.

"TableMyCrossConfig": {
  "Type": "AWS::DynamoDB::Table",
  "Properties": {
    "TableName": "MyCrossConfig",
    "AttributeDefinitions": [
      {
        "AttributeName": "id",
        "AttributeType": "S"
      }
    ],
    "KeySchema": [
      {
        "AttributeName": "id",
        "KeyType": "HASH"
      }
    ],
    "ProvisionedThroughput": {
      "ReadCapacityUnits": "5",
      "WriteCapacityUnits": "5"
    }
  }
},
"alarmTargetTrackingtableMyCrossConfigProvisionedCapacityLowdfcae8d90ee2487a8e59c7bc0f9f6bd9": {
  "Type": "AWS::CloudWatch::Alarm",
  "Properties": {
    "ActionsEnabled": "true",
    "AlarmDescription": {
      "Fn::Join": [
        "",
        [
          "DO NOT EDIT OR DELETE. For TargetTrackingScaling policy arn:aws:autoscaling:",
          {
            "Ref": "AWS::Region"
          },
          ":",
          {
            "Ref": "AWS::AccountId"
          },
          ":scalingPolicy:7558858e-b58c-455c-be34-6de387a0c6d1:resource/dynamodb/table/MyCrossConfig:policyName/DynamoDBReadCapacityUtilization:table/MyCrossConfig."
        ]
      ]
    },
    "ComparisonOperator": "LessThanThreshold",
    "EvaluationPeriods": "3",
    "MetricName": "ProvisionedReadCapacityUnits",
    "Namespace": "AWS/DynamoDB",
    "Period": "300",
    "Statistic": "Average",
    "Threshold": "5.0",
    "AlarmActions": [
      {
        "Fn::Join": [
          "",
          [
            "arn:aws:autoscaling:",
            {
              "Ref": "AWS::Region"
            },
            ":",
            {
              "Ref": "AWS::AccountId"
            },
            ":scalingPolicy:7558858e-b58c-455c-be34-6de387a0c6d1:resource/dynamodb/table/MyCrossConfig:policyName/DynamoDBReadCapacityUtilization:table/MyCrossConfig"
          ]
        ]
      }
    ],
    "Dimensions": [
      {
        "Name": "TableName",
        "Value": "MyCrossConfig"
      }
    ]
  }
}

s3 bucket AWS::S3::Bucket

  • This example creates a Bucket with name configbucket- + AWS::AccountId

"ConfigBucket": {
  "Type": "AWS::S3::Bucket",
  "Properties": {
    "BucketName": {
      "Fn::Join": [
        "",
        [
          "configbucket-",
          {
            "Ref": "AWS::AccountId"
          }
        ]
      ]
    }
  },
  "DeletionPolicy": "Delete"
}

Now you need to put altogether, make the reference in the template, Etc.

Hope it helps!

like image 39
Ele Avatar answered Sep 17 '25 19:09

Ele