From 45267673de65d5ac06e9adda2d5801bfd58a86de Mon Sep 17 00:00:00 2001 From: Eric Beard Date: Tue, 14 May 2024 14:09:52 -0700 Subject: [PATCH 1/2] Deploying EC2 instance samples --- .gitignore | 1 + APIGateway/apigateway_lambda_integration.json | 512 ++++++++-------- APIGateway/apigateway_lambda_integration.yaml | 158 +++++ APIGateway/handler.py | 30 + .../AutoScalingMultiAZWithNotifications.json | 551 +++++------------- .../AutoScalingMultiAZWithNotifications.yaml | 396 +++---------- Config/Config.yaml | 2 +- EC2/InstanceWithCfnInit.json | 85 +++ EC2/InstanceWithCfnInit.yaml | 66 +++ scripts/guard-all.sh | 2 - scripts/guard-single.sh | 1 - scripts/rules.guard | 30 + scripts/test-all.sh | 3 +- 13 files changed, 839 insertions(+), 998 deletions(-) create mode 100644 APIGateway/apigateway_lambda_integration.yaml create mode 100644 APIGateway/handler.py create mode 100644 EC2/InstanceWithCfnInit.json create mode 100644 EC2/InstanceWithCfnInit.yaml diff --git a/.gitignore b/.gitignore index b402f4af..d85551bf 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ .vscode/ .env .venv +params.yml diff --git a/APIGateway/apigateway_lambda_integration.json b/APIGateway/apigateway_lambda_integration.json index ab312075..7ec674f3 100644 --- a/APIGateway/apigateway_lambda_integration.json +++ b/APIGateway/apigateway_lambda_integration.json @@ -1,291 +1,265 @@ { - "AWSTemplateFormatVersion": "2010-09-09", - "Parameters": { - "ApiType": { - "Description": "The Endpoint type for RESTApi", - "Type": "String", - "Default": "REGIONAL", - "AllowedValues": [ - "EDGE", - "REGIONAL", - "PRIVATE" - ] - }, - "ApigatewayTimeout": { - "Description": "ApiGateway Backend Integration timeout in milliseconds", - "Type": "Number", - "Default": "29000", - "MinValue": "50", - "MaxValue": "29000" - }, - "LambdaFunctionName": { - "Description": "The Name for the Lambda Function", - "Type": "String", - "Default": "My-Function" - } - }, - "Resources": { - "RestApi": { - "Type": "AWS::ApiGateway::RestApi", - "Properties": { - "Description": "My Rest API", - "Name": "MyApi", - "EndpointConfiguration": { - "Types": [ - { - "Ref": "ApiType" - } - ] - } - } - }, - "ApiResource": { - "Type": "AWS::ApiGateway::Resource", - "Properties": { - "ParentId": { - "Fn::GetAtt": [ - "RestApi", - "RootResourceId" - ] - }, - "RestApiId": { - "Ref": "RestApi" + "AWSTemplateFormatVersion": "2010-09-09", + "Parameters": { + "ApiType": { + "Description": "The Endpoint type for RESTApi", + "Type": "String", + "AllowedValues": [ + "EDGE", + "REGIONAL", + "PRIVATE" + ], + "Default": "REGIONAL" }, - "PathPart": "{city}" - } - }, - "RequestModel": { - "Type": "AWS::ApiGateway::Model", - "Properties": { - "ContentType": "application/json", - "Name": "MyModel", - "RestApiId": { - "Ref": "RestApi" + "ApigatewayTimeout": { + "Description": "ApiGateway Backend Integration timeout in milliseconds", + "Type": "Number", + "Default": "29000", + "MinValue": "50", + "MaxValue": "29000" }, - "Schema": { - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "MyModel", - "type": "object", - "properties": { - "callerName": { - "type": "string" - } - } + "LambdaFunctionName": { + "Description": "The Name for the Lambda Function", + "Type": "String", + "Default": "My-APIGW-Integ-Function" } - } }, - "ApiMethod": { - "Type": "AWS::ApiGateway::Method", - "Properties": { - "HttpMethod": "ANY", - "AuthorizationType": "NONE", - "RequestParameters": { - "method.request.path.city": "true", - "method.request.querystring.time": "true", - "method.request.header.day": "true" + "Resources": { + "RestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Description": "My Rest API", + "Name": "MyApi", + "EndpointConfiguration": { + "Types": [ + { + "Ref": "ApiType" + } + ] + } + } }, - "MethodResponses": [ - { - "StatusCode": "200" - } - ], - "Integration": { - "IntegrationHttpMethod": "POST", - "Type": "AWS", - "TimeoutInMillis": { - "Ref": "ApigatewayTimeout" - }, - "Uri": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":apigateway:", - { - "Ref": "AWS::Region" + "ApiResource": { + "Type": "AWS::ApiGateway::Resource", + "Properties": { + "ParentId": { + "Fn::GetAtt": [ + "RestApi", + "RootResourceId" + ] }, - ":lambda:path/2015-03-31/functions/", - { - "Fn::GetAtt": [ - "LambdaFunction", - "Arn" - ] + "RestApiId": { + "Ref": "RestApi" }, - "/invocations" - ] - ] - }, - "RequestTemplates": { - "application/json": { - "Fn::Join": [ - "", - [ - "\n #set($inputRoot = $input.path('$'))", - "\n{", - "\n \"city\": \"$input.params('city')\",", - "\n \"time\": \"$input.params('time')\",", - "\n \"day\": \"$input.params('day')\",", - "\n \"name\": \"$inputRoot.callerName\"", - "\n}" - ] - ] + "PathPart": "{city}" } - }, - "IntegrationResponses": [ - { - "StatusCode": "200" - } - ] - }, - "ResourceId": { - "Ref": "ApiResource" }, - "RestApiId": { - "Ref": "RestApi" + "RequestModel": { + "Type": "AWS::ApiGateway::Model", + "Properties": { + "ContentType": "application/json", + "Name": "MyModel", + "RestApiId": { + "Ref": "RestApi" + }, + "Schema": { + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "MyModel", + "type": "object", + "properties": { + "callerName": { + "type": "string" + } + } + } + } }, - "RequestModels": { - "application/json": { - "Ref": "RequestModel" - } - } - } - }, - "LambdaFunction": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ZipFile": { - "Fn::Join": [ - "\n", - [ - "import json", - "days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']", - "times = ['morning', 'afternoon', 'evening', 'night', 'day']", - "def lambda_handler(event, context):", - " name = event.get(\"name\", \"you\")", - " city = event.get(\"city\", \"world\")", - " try:", - " if (times.index(event.get(\"time\", \"None\"))) >-1:", - " time = event.get(\"time\")", - " except ValueError:", - " time = \"day\"", - " try:", - " if (days.index(event.get(\"day\",\"None\"))) >-1:", - " day = event['day']", - " except ValueError:", - " day = None", - " greetings = \"Good {time}, {name} of {city}. \".format(time=time, name=name, city=city)", - " if day:", - " greetings += \"Happy {}\".format(day)", - " return {", - " 'statusCode': 200,", - " 'body': json.dumps(greetings)", - " }" - ] - ] - } + "ApiMethod": { + "Type": "AWS::ApiGateway::Method", + "Properties": { + "HttpMethod": "ANY", + "AuthorizationType": "NONE", + "RequestParameters": { + "method.request.path.city": "true", + "method.request.querystring.time": "true", + "method.request.header.day": "true" + }, + "MethodResponses": [ + { + "StatusCode": "200" + } + ], + "Integration": { + "IntegrationHttpMethod": "POST", + "Type": "AWS", + "TimeoutInMillis": { + "Ref": "ApigatewayTimeout" + }, + "Uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "LambdaFunction", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "RequestTemplates": { + "application/json": "#set($inputRoot = $input.path('$'))\n {\n \"city\": \"$input.params('city')\",\n \"time\": \"$input.params('time')\",\n \"day\": \"$input.params('day')\",\n \"name\": \"$inputRoot.callerName\"\n }\n" + }, + "IntegrationResponses": [ + { + "StatusCode": "200" + } + ] + }, + "ResourceId": { + "Ref": "ApiResource" + }, + "RestApiId": { + "Ref": "RestApi" + }, + "RequestModels": { + "application/json": { + "Ref": "RequestModel" + } + } + } }, - "Handler": "index.lambda_handler", - "FunctionName": { - "Ref": "LambdaFunctionName" + "LambdaFunction": { + "Type": "AWS::Lambda::Function", + "Metadata": { + "cfn-lint": { + "config": { + "ignore_checks": [ + "E3012" + ] + } + }, + "guard": { + "SuppressedRules": [ + "LAMBDA_INSIDE_VPC" + ] + } + }, + "Properties": { + "Code": { + "ZipFile": { + "Rain::Embed": "handler.py" + } + }, + "Handler": "index.lambda_handler", + "FunctionName": { + "Ref": "LambdaFunctionName" + }, + "MemorySize": "128", + "Runtime": "python3.12", + "Timeout": "10", + "Role": { + "Fn::GetAtt": [ + "LambdaIamRole", + "Arn" + ] + } + } }, - "MemorySize": "128", - "Runtime": "python3.8", - "Timeout": "10", - "Role": { - "Fn::GetAtt": [ - "LambdaIamRole", - "Arn" - ] - } - } - }, - "LambdaIamRole": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "Service": [ - "lambda.amazonaws.com" + "LambdaIamRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + }, + "Action": [ + "sts:AssumeRole" + ] + } + ] + }, + "RoleName": "LambdaRole", + "Policies": [ + { + "PolicyName": "LambdaApipolicy", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "logs:CreateLogGroup" + ], + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:*" + } + }, + { + "Effect": "Allow", + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}:*" + } + } + ] + } + } ] - }, - "Action": [ - "sts:AssumeRole" - ] } - ] }, - "RoleName": "LambdaRole", - "Policies": [ - { - "PolicyName": "LambdaApipolicy", - "PolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "logs:CreateLogGroup" - ], - "Resource": { - "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:*" - } + "LambdaApiGatewayInvoke": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "LambdaFunction", + "Arn" + ] }, - { - "Effect": "Allow", - "Action": [ - "logs:CreateLogStream", - "logs:PutLogEvents" - ], - "Resource": { - "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}:*" - } + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:aws:execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "RestApi" + }, + "/*/*/*" + ] + ] } - ] } - } - ] - } - }, - "LambdaApiGatewayInvoke": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:InvokeFunction", - "FunctionName": { - "Fn::GetAtt": [ - "LambdaFunction", - "Arn" - ] - }, - "Principal": "apigateway.amazonaws.com", - "SourceArn": { - "Fn::Join": [ - "", - [ - "arn:aws:execute-api:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":", - { - "Ref": "RestApi" - }, - "/*/*/*" - ] - ] } - } } - } } diff --git a/APIGateway/apigateway_lambda_integration.yaml b/APIGateway/apigateway_lambda_integration.yaml new file mode 100644 index 00000000..4d4185aa --- /dev/null +++ b/APIGateway/apigateway_lambda_integration.yaml @@ -0,0 +1,158 @@ +AWSTemplateFormatVersion: "2010-09-09" + +Parameters: + ApiType: + Description: The Endpoint type for RESTApi + Type: String + AllowedValues: + - EDGE + - REGIONAL + - PRIVATE + Default: REGIONAL + + ApigatewayTimeout: + Description: ApiGateway Backend Integration timeout in milliseconds + Type: Number + Default: "29000" + MinValue: "50" + MaxValue: "29000" + + LambdaFunctionName: + Description: The Name for the Lambda Function + Type: String + Default: My-APIGW-Integ-Function + +Resources: + RestApi: + Type: AWS::ApiGateway::RestApi + Properties: + Description: My Rest API + Name: MyApi + EndpointConfiguration: + Types: + - !Ref ApiType + + ApiResource: + Type: AWS::ApiGateway::Resource + Properties: + ParentId: !GetAtt RestApi.RootResourceId + RestApiId: !Ref RestApi + PathPart: '{city}' + + RequestModel: + Type: AWS::ApiGateway::Model + Properties: + ContentType: application/json + Name: MyModel + RestApiId: !Ref RestApi + Schema: + $schema: http://json-schema.org/draft-04/schema# + title: MyModel + type: object + properties: + callerName: + type: string + + ApiMethod: + Type: AWS::ApiGateway::Method + Properties: + HttpMethod: ANY + AuthorizationType: NONE + RequestParameters: + method.request.path.city: "true" + method.request.querystring.time: "true" + method.request.header.day: "true" + MethodResponses: + - StatusCode: "200" + Integration: + IntegrationHttpMethod: POST + Type: AWS + TimeoutInMillis: !Ref ApigatewayTimeout + Uri: !Join + - "" + - - 'arn:' + - !Ref AWS::Partition + - ':apigateway:' + - !Ref AWS::Region + - :lambda:path/2015-03-31/functions/ + - !GetAtt LambdaFunction.Arn + - /invocations + RequestTemplates: + application/json: | + #set($inputRoot = $input.path('$')) + { + "city": "$input.params('city')", + "time": "$input.params('time')", + "day": "$input.params('day')", + "name": "$inputRoot.callerName" + } + IntegrationResponses: + - StatusCode: "200" + ResourceId: !Ref ApiResource + RestApiId: !Ref RestApi + RequestModels: + application/json: !Ref RequestModel + + LambdaFunction: + Type: AWS::Lambda::Function + Metadata: + cfn-lint: + config: + ignore_checks: + - E3012 + guard: + SuppressedRules: + - LAMBDA_INSIDE_VPC + Properties: + Code: + ZipFile: !Rain::Embed handler.py + Handler: index.lambda_handler + FunctionName: !Ref LambdaFunctionName + MemorySize: "128" + Runtime: python3.12 + Timeout: "10" + Role: !GetAtt LambdaIamRole.Arn + + LambdaIamRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Service: + - lambda.amazonaws.com + Action: + - sts:AssumeRole + RoleName: LambdaRole + Policies: + - PolicyName: LambdaApipolicy + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - logs:CreateLogGroup + Resource: !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:* + - Effect: Allow + Action: + - logs:CreateLogStream + - logs:PutLogEvents + Resource: !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}:* + + LambdaApiGatewayInvoke: + Type: AWS::Lambda::Permission + Properties: + Action: lambda:InvokeFunction + FunctionName: !GetAtt LambdaFunction.Arn + Principal: apigateway.amazonaws.com + SourceArn: !Join + - "" + - - 'arn:aws:execute-api:' + - !Ref AWS::Region + - ':' + - !Ref AWS::AccountId + - ':' + - !Ref RestApi + - /*/*/* diff --git a/APIGateway/handler.py b/APIGateway/handler.py new file mode 100644 index 00000000..64fb689f --- /dev/null +++ b/APIGateway/handler.py @@ -0,0 +1,30 @@ +"""Generate a greeting as a demo of an API""" + +import json + +DAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] +TIMES = ['morning', 'afternoon', 'evening', 'night', 'day'] + + +def lambda_handler(event, _): + """ + Lambda handler + """ + name = event.get("name", "you") + city = event.get("city", "world") + time = event.get("time", "day") + day = event.get("day", None) + + if time not in TIMES: + time = "day" + if day and day not in DAYS: + day = None + + greetings = f"Good {time}, {name} of {city}." + if day: + greetings += f" Happy {day}" + + return { + 'statusCode': 200, + 'body': json.dumps(greetings) + } diff --git a/AutoScaling/AutoScalingMultiAZWithNotifications.json b/AutoScaling/AutoScalingMultiAZWithNotifications.json index 9edcc8bd..6d521c1c 100644 --- a/AutoScaling/AutoScalingMultiAZWithNotifications.json +++ b/AutoScaling/AutoScalingMultiAZWithNotifications.json @@ -1,69 +1,11 @@ { "AWSTemplateFormatVersion": "2010-09-09", "Description": "AWS CloudFormation Sample Template AutoScalingMultiAZWithNotifications: Create a multi-az, load balanced and Auto Scaled sample web site running on an Apache Web Server. The application is configured to span all Availability Zones in the region and is Auto-Scaled based on the CPU utilization of the web servers. Notifications will be sent to the operator email address on scaling events. The instances are load balanced with a simple health check against the default web page. **WARNING** This template creates one or more Amazon EC2 instances and an Elastic Load Balancer. You will be billed for the AWS resources used if you create a stack from this template.", - "Metadata": { - "License": "Apache-2.0" - }, "Parameters": { "InstanceType": { "Description": "WebServer EC2 instance type", "Type": "String", - "AllowedValues": [ - "t1.micro", - "t2.nano", - "t2.micro", - "t2.small", - "t2.medium", - "t2.large", - "m1.small", - "m1.medium", - "m1.large", - "m1.xlarge", - "m2.xlarge", - "m2.2xlarge", - "m2.4xlarge", - "m3.medium", - "m3.large", - "m3.xlarge", - "m3.2xlarge", - "m4.large", - "m4.xlarge", - "m4.2xlarge", - "m4.4xlarge", - "m4.10xlarge", - "c1.medium", - "c1.xlarge", - "c3.large", - "c3.xlarge", - "c3.2xlarge", - "c3.4xlarge", - "c3.8xlarge", - "c4.large", - "c4.xlarge", - "c4.2xlarge", - "c4.4xlarge", - "c4.8xlarge", - "g2.2xlarge", - "g2.8xlarge", - "r3.large", - "r3.xlarge", - "r3.2xlarge", - "r3.4xlarge", - "r3.8xlarge", - "i2.xlarge", - "i2.2xlarge", - "i2.4xlarge", - "i2.8xlarge", - "d2.xlarge", - "d2.2xlarge", - "d2.4xlarge", - "d2.8xlarge", - "hs1.8xlarge", - "cr1.8xlarge", - "cc2.8xlarge" - ], - "Default": "t2.small", - "ConstraintDescription": "must be a valid EC2 instance type." + "Default": "t2.micro" }, "OperatorEMail": { "Description": "EMail address to notify if there are any scaling operations", @@ -84,6 +26,26 @@ "MaxLength": 18, "AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})", "ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x." + }, + "LatestAmiId": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-arm64" + }, + "KmsKeyArn": { + "Description": "KMS Key ARN to encrypt data", + "Type": "String" + }, + "CertificateArn": { + "Description": "Certificate ARN for HTTPS", + "Type": "String" + }, + "SecurityGroups": { + "Description": "Security Groups to be used", + "Type": "List" + }, + "Subnets": { + "Description": "Subnets to be used", + "Type": "List" } }, "Mappings": { @@ -127,240 +89,15 @@ "cn-north-1": { "Examples": "https://s3.cn-north-1.amazonaws.com.cn/cloudformation-examples-cn-north-1" } - }, - "AWSInstanceType2Arch": { - "t1.micro": { - "Arch": "PV64" - }, - "t2.nano": { - "Arch": "HVM64" - }, - "t2.micro": { - "Arch": "HVM64" - }, - "t2.small": { - "Arch": "HVM64" - }, - "t2.medium": { - "Arch": "HVM64" - }, - "t2.large": { - "Arch": "HVM64" - }, - "m1.small": { - "Arch": "PV64" - }, - "m1.medium": { - "Arch": "PV64" - }, - "m1.large": { - "Arch": "PV64" - }, - "m1.xlarge": { - "Arch": "PV64" - }, - "m2.xlarge": { - "Arch": "PV64" - }, - "m2.2xlarge": { - "Arch": "PV64" - }, - "m2.4xlarge": { - "Arch": "PV64" - }, - "m3.medium": { - "Arch": "HVM64" - }, - "m3.large": { - "Arch": "HVM64" - }, - "m3.xlarge": { - "Arch": "HVM64" - }, - "m3.2xlarge": { - "Arch": "HVM64" - }, - "m4.large": { - "Arch": "HVM64" - }, - "m4.xlarge": { - "Arch": "HVM64" - }, - "m4.2xlarge": { - "Arch": "HVM64" - }, - "m4.4xlarge": { - "Arch": "HVM64" - }, - "m4.10xlarge": { - "Arch": "HVM64" - }, - "c1.medium": { - "Arch": "PV64" - }, - "c1.xlarge": { - "Arch": "PV64" - }, - "c3.large": { - "Arch": "HVM64" - }, - "c3.xlarge": { - "Arch": "HVM64" - }, - "c3.2xlarge": { - "Arch": "HVM64" - }, - "c3.4xlarge": { - "Arch": "HVM64" - }, - "c3.8xlarge": { - "Arch": "HVM64" - }, - "c4.large": { - "Arch": "HVM64" - }, - "c4.xlarge": { - "Arch": "HVM64" - }, - "c4.2xlarge": { - "Arch": "HVM64" - }, - "c4.4xlarge": { - "Arch": "HVM64" - }, - "c4.8xlarge": { - "Arch": "HVM64" - }, - "g2.2xlarge": { - "Arch": "HVMG2" - }, - "g2.8xlarge": { - "Arch": "HVMG2" - }, - "r3.large": { - "Arch": "HVM64" - }, - "r3.xlarge": { - "Arch": "HVM64" - }, - "r3.2xlarge": { - "Arch": "HVM64" - }, - "r3.4xlarge": { - "Arch": "HVM64" - }, - "r3.8xlarge": { - "Arch": "HVM64" - }, - "i2.xlarge": { - "Arch": "HVM64" - }, - "i2.2xlarge": { - "Arch": "HVM64" - }, - "i2.4xlarge": { - "Arch": "HVM64" - }, - "i2.8xlarge": { - "Arch": "HVM64" - }, - "d2.xlarge": { - "Arch": "HVM64" - }, - "d2.2xlarge": { - "Arch": "HVM64" - }, - "d2.4xlarge": { - "Arch": "HVM64" - }, - "d2.8xlarge": { - "Arch": "HVM64" - }, - "hi1.4xlarge": { - "Arch": "HVM64" - }, - "hs1.8xlarge": { - "Arch": "HVM64" - }, - "cr1.8xlarge": { - "Arch": "HVM64" - }, - "cc2.8xlarge": { - "Arch": "HVM64" - } - }, - "AWSRegionArch2AMI": { - "us-east-1": { - "PV64": "ami-2a69aa47", - "HVM64": "ami-6869aa05", - "HVMG2": "ami-50b4f047" - }, - "us-west-2": { - "PV64": "ami-7f77b31f", - "HVM64": "ami-7172b611", - "HVMG2": "ami-002bf460" - }, - "us-west-1": { - "PV64": "ami-a2490dc2", - "HVM64": "ami-31490d51", - "HVMG2": "ami-699ad409" - }, - "eu-west-1": { - "PV64": "ami-4cdd453f", - "HVM64": "ami-f9dd458a", - "HVMG2": "ami-f0e0a483" - }, - "eu-central-1": { - "PV64": "ami-6527cf0a", - "HVM64": "ami-ea26ce85", - "HVMG2": "ami-d9d62ab6" - }, - "ap-northeast-1": { - "PV64": "ami-3e42b65f", - "HVM64": "ami-374db956", - "HVMG2": "ami-78ba6619" - }, - "ap-northeast-2": { - "PV64": "NOT_SUPPORTED", - "HVM64": "ami-2b408b45", - "HVMG2": "NOT_SUPPORTED" - }, - "ap-southeast-1": { - "PV64": "ami-df9e4cbc", - "HVM64": "ami-a59b49c6", - "HVMG2": "ami-56e84c35" - }, - "ap-southeast-2": { - "PV64": "ami-63351d00", - "HVM64": "ami-dc361ebf", - "HVMG2": "ami-2589b946" - }, - "ap-south-1": { - "PV64": "NOT_SUPPORTED", - "HVM64": "ami-ffbdd790", - "HVMG2": "ami-f7354198" - }, - "us-east-2": { - "PV64": "NOT_SUPPORTED", - "HVM64": "ami-f6035893", - "HVMG2": "NOT_SUPPORTED" - }, - "sa-east-1": { - "PV64": "ami-1ad34676", - "HVM64": "ami-6dd04501", - "HVMG2": "NOT_SUPPORTED" - }, - "cn-north-1": { - "PV64": "ami-77559f1a", - "HVM64": "ami-8e6aa0e3", - "HVMG2": "NOT_SUPPORTED" - } } }, "Resources": { "NotificationTopic": { "Type": "AWS::SNS::Topic", "Properties": { + "DisplayName": { + "Fn::Sub": "${AWS::StackName}-NotificationTopic" + }, "Subscription": [ { "Endpoint": { @@ -368,58 +105,15 @@ }, "Protocol": "email" } - ] - } - }, - "WebServerGroup": { - "CreationPolicy": { - "ResourceSignal": { - "Timeout": "PT15M", - "Count": 1 - } - }, - "UpdatePolicy": { - "AutoScalingRollingUpdate": { - "MinInstancesInService": 1, - "MaxBatchSize": 1, - "PauseTime": "PT15M", - "WaitOnResourceSignals": true - } - }, - "Type": "AWS::AutoScaling::AutoScalingGroup", - "Properties": { - "AvailabilityZones": { - "Fn::GetAZs": null - }, - "LaunchConfigurationName": { - "Ref": "LaunchConfig" - }, - "MinSize": "1", - "MaxSize": "3", - "LoadBalancerNames": [ - { - "Ref": "ElasticLoadBalancer" - } ], - "NotificationConfigurations": [ - { - "TopicARN": { - "Ref": "NotificationTopic" - }, - "NotificationTypes": [ - "autoscaling:EC2_INSTANCE_LAUNCH", - "autoscaling:EC2_INSTANCE_LAUNCH_ERROR", - "autoscaling:EC2_INSTANCE_TERMINATE", - "autoscaling:EC2_INSTANCE_TERMINATE_ERROR" - ] - } - ] + "KmsMasterKeyId": { + "Ref": "KmsKeyArn" + } } }, - "LaunchConfig": { - "Type": "AWS::AutoScaling::LaunchConfiguration", + "LaunchTemplate": { + "Type": "AWS::EC2::LaunchTemplate", "Metadata": { - "Comment": "Install a simple application", "AWS::CloudFormation::Init": { "config": { "packages": { @@ -454,22 +148,7 @@ }, "/etc/cfn/cfn-hup.conf": { "content": { - "Fn::Join": [ - "", - [ - "[main] ", - "stack=", - { - "Ref": "AWS::StackId" - }, - " ", - "region=", - { - "Ref": "AWS::Region" - }, - " " - ] - ] + "Fn::Sub": "[main]\nstack=${AWS::StackId}\nregion=${AWS::Region}\n" }, "mode": "000400", "owner": "root", @@ -477,26 +156,7 @@ }, "/etc/cfn/hooks.d/cfn-auto-reloader.conf": { "content": { - "Fn::Join": [ - "", - [ - "[cfn-auto-reloader-hook] ", - "triggers=post.update ", - "path=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init ", - "action=/opt/aws/bin/cfn-init -v ", - " --stack ", - { - "Ref": "AWS::StackName" - }, - " --resource LaunchConfig ", - " --region ", - { - "Ref": "AWS::Region" - }, - " ", - "runas=root " - ] - ] + "Fn::Sub": "[cfn-auto-reloader-hook]\ntriggers=post.update\npath=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init\naction=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --region ${AWS::Region}\nrunas=root\n" } } }, @@ -520,66 +180,114 @@ } }, "Properties": { - "KeyName": { - "Ref": "KeyName" + "LaunchTemplateName": { + "Fn::Sub": "${AWS::StackName}-LaunchTemplate" }, - "ImageId": { - "Fn::FindInMap": [ - "AWSRegionArch2AMI", + "LaunchTemplateData": { + "ImageId": { + "Ref": "LatestAmiId" + }, + "InstanceType": { + "Ref": "InstanceType" + }, + "SecurityGroupIds": { + "Ref": "SecurityGroups" + }, + "KeyName": { + "Ref": "KeyName" + }, + "BlockDeviceMappings": [ { - "Ref": "AWS::Region" - }, + "DeviceName": "/dev/sda1", + "Ebs": { + "VolumeSize": 32 + } + } + ], + "UserData": { + "Fn::Base64": { + "Fn::Sub": "#!/bin/bash\n/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --region ${AWS::Region}\n/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerGroup --region ${AWS::Region}\n" + } + }, + "TagSpecifications": [ { - "Fn::FindInMap": [ - "AWSInstanceType2Arch", + "ResourceType": "instance", + "Tags": [ { - "Ref": "InstanceType" - }, - "Arch" + "Key": "Name", + "Value": { + "Fn::Sub": "${AWS::StackName}-Instance" + } + } ] } ] + } + } + }, + "WebServerGroup": { + "CreationPolicy": { + "ResourceSignal": { + "Timeout": "PT5M", + "Count": 1 + } + }, + "UpdatePolicy": { + "AutoScalingRollingUpdate": { + "MinInstancesInService": 1, + "MaxBatchSize": 1, + "PauseTime": "PT5M", + "WaitOnResourceSignals": true + } + }, + "Type": "AWS::AutoScaling::AutoScalingGroup", + "Metadata": { + "cfn-lint": { + "config": { + "ignore_checks": [ + "E3014" + ] + } + } + }, + "Properties": { + "AvailabilityZones": { + "Fn::GetAZs": null + }, + "LaunchTemplate": { + "LaunchTemplateId": { + "Ref": "LaunchTemplate" + }, + "Version": { + "Fn::GetAtt": [ + "LaunchTemplate", + "LatestVersionNumber" + ] + } }, - "SecurityGroups": [ + "MinSize": "1", + "MaxSize": "3", + "LoadBalancerNames": [ { - "Ref": "InstanceSecurityGroup" + "Ref": "ElasticLoadBalancer" } ], - "InstanceType": { - "Ref": "InstanceType" - }, - "UserData": { - "Fn::Base64": { - "Fn::Join": [ - "", - [ - "#!/bin/bash -xe ", - "yum update -y aws-cfn-bootstrap ", - "/opt/aws/bin/cfn-init -v ", - " --stack ", - { - "Ref": "AWS::StackName" - }, - " --resource LaunchConfig ", - " --region ", - { - "Ref": "AWS::Region" - }, - " ", - "/opt/aws/bin/cfn-signal -e $? ", - " --stack ", - { - "Ref": "AWS::StackName" - }, - " --resource WebServerGroup ", - " --region ", - { - "Ref": "AWS::Region" - }, - " " - ] + "NotificationConfigurations": [ + { + "TopicARN": { + "Ref": "NotificationTopic" + }, + "NotificationTypes": [ + "autoscaling:EC2_INSTANCE_LAUNCH", + "autoscaling:EC2_INSTANCE_LAUNCH_ERROR", + "autoscaling:EC2_INSTANCE_TERMINATE", + "autoscaling:EC2_INSTANCE_TERMINATE_ERROR" ] } + ], + "HealthCheckType": "ELB", + "VPCZoneIdentifier": { + "Ref": "Subnets" } } }, @@ -668,7 +376,10 @@ { "LoadBalancerPort": "80", "InstancePort": "80", - "Protocol": "HTTP" + "Protocol": "HTTP", + "SSLCertificateId": { + "Ref": "CertificateArn" + } } ], "HealthCheck": { @@ -728,7 +439,7 @@ "Fn::Join": [ "", [ - "http://", + "https://", { "Fn::GetAtt": [ "ElasticLoadBalancer", diff --git a/AutoScaling/AutoScalingMultiAZWithNotifications.yaml b/AutoScaling/AutoScalingMultiAZWithNotifications.yaml index 557fba8f..e14f32f5 100644 --- a/AutoScaling/AutoScalingMultiAZWithNotifications.yaml +++ b/AutoScaling/AutoScalingMultiAZWithNotifications.yaml @@ -2,68 +2,11 @@ AWSTemplateFormatVersion: "2010-09-09" Description: 'AWS CloudFormation Sample Template AutoScalingMultiAZWithNotifications: Create a multi-az, load balanced and Auto Scaled sample web site running on an Apache Web Server. The application is configured to span all Availability Zones in the region and is Auto-Scaled based on the CPU utilization of the web servers. Notifications will be sent to the operator email address on scaling events. The instances are load balanced with a simple health check against the default web page. **WARNING** This template creates one or more Amazon EC2 instances and an Elastic Load Balancer. You will be billed for the AWS resources used if you create a stack from this template.' -Metadata: - License: Apache-2.0 - Parameters: InstanceType: Description: WebServer EC2 instance type Type: String - AllowedValues: - - t1.micro - - t2.nano - - t2.micro - - t2.small - - t2.medium - - t2.large - - m1.small - - m1.medium - - m1.large - - m1.xlarge - - m2.xlarge - - m2.2xlarge - - m2.4xlarge - - m3.medium - - m3.large - - m3.xlarge - - m3.2xlarge - - m4.large - - m4.xlarge - - m4.2xlarge - - m4.4xlarge - - m4.10xlarge - - c1.medium - - c1.xlarge - - c3.large - - c3.xlarge - - c3.2xlarge - - c3.4xlarge - - c3.8xlarge - - c4.large - - c4.xlarge - - c4.2xlarge - - c4.4xlarge - - c4.8xlarge - - g2.2xlarge - - g2.8xlarge - - r3.large - - r3.xlarge - - r3.2xlarge - - r3.4xlarge - - r3.8xlarge - - i2.xlarge - - i2.2xlarge - - i2.4xlarge - - i2.8xlarge - - d2.xlarge - - d2.2xlarge - - d2.4xlarge - - d2.8xlarge - - hs1.8xlarge - - cr1.8xlarge - - cc2.8xlarge - Default: t2.small - ConstraintDescription: must be a valid EC2 instance type. + Default: t2.micro OperatorEMail: Description: EMail address to notify if there are any scaling operations @@ -85,6 +28,26 @@ Parameters: AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2}) ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x. + LatestAmiId: + Type: AWS::SSM::Parameter::Value + Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-arm64 + + KmsKeyArn: + Description: KMS Key ARN to encrypt data + Type: String + + CertificateArn: + Description: Certificate ARN for HTTPS + Type: String + + SecurityGroups: + Description: Security Groups to be used + Type: List + + Subnets: + Description: Subnets to be used + Type: List + Mappings: Region2Examples: us-east-1: @@ -114,207 +77,19 @@ Mappings: cn-north-1: Examples: https://s3.cn-north-1.amazonaws.com.cn/cloudformation-examples-cn-north-1 - AWSInstanceType2Arch: - t1.micro: - Arch: PV64 - t2.nano: - Arch: HVM64 - t2.micro: - Arch: HVM64 - t2.small: - Arch: HVM64 - t2.medium: - Arch: HVM64 - t2.large: - Arch: HVM64 - m1.small: - Arch: PV64 - m1.medium: - Arch: PV64 - m1.large: - Arch: PV64 - m1.xlarge: - Arch: PV64 - m2.xlarge: - Arch: PV64 - m2.2xlarge: - Arch: PV64 - m2.4xlarge: - Arch: PV64 - m3.medium: - Arch: HVM64 - m3.large: - Arch: HVM64 - m3.xlarge: - Arch: HVM64 - m3.2xlarge: - Arch: HVM64 - m4.large: - Arch: HVM64 - m4.xlarge: - Arch: HVM64 - m4.2xlarge: - Arch: HVM64 - m4.4xlarge: - Arch: HVM64 - m4.10xlarge: - Arch: HVM64 - c1.medium: - Arch: PV64 - c1.xlarge: - Arch: PV64 - c3.large: - Arch: HVM64 - c3.xlarge: - Arch: HVM64 - c3.2xlarge: - Arch: HVM64 - c3.4xlarge: - Arch: HVM64 - c3.8xlarge: - Arch: HVM64 - c4.large: - Arch: HVM64 - c4.xlarge: - Arch: HVM64 - c4.2xlarge: - Arch: HVM64 - c4.4xlarge: - Arch: HVM64 - c4.8xlarge: - Arch: HVM64 - g2.2xlarge: - Arch: HVMG2 - g2.8xlarge: - Arch: HVMG2 - r3.large: - Arch: HVM64 - r3.xlarge: - Arch: HVM64 - r3.2xlarge: - Arch: HVM64 - r3.4xlarge: - Arch: HVM64 - r3.8xlarge: - Arch: HVM64 - i2.xlarge: - Arch: HVM64 - i2.2xlarge: - Arch: HVM64 - i2.4xlarge: - Arch: HVM64 - i2.8xlarge: - Arch: HVM64 - d2.xlarge: - Arch: HVM64 - d2.2xlarge: - Arch: HVM64 - d2.4xlarge: - Arch: HVM64 - d2.8xlarge: - Arch: HVM64 - hi1.4xlarge: - Arch: HVM64 - hs1.8xlarge: - Arch: HVM64 - cr1.8xlarge: - Arch: HVM64 - cc2.8xlarge: - Arch: HVM64 - - AWSRegionArch2AMI: - us-east-1: - PV64: ami-2a69aa47 - HVM64: ami-6869aa05 - HVMG2: ami-50b4f047 - us-west-2: - PV64: ami-7f77b31f - HVM64: ami-7172b611 - HVMG2: ami-002bf460 - us-west-1: - PV64: ami-a2490dc2 - HVM64: ami-31490d51 - HVMG2: ami-699ad409 - eu-west-1: - PV64: ami-4cdd453f - HVM64: ami-f9dd458a - HVMG2: ami-f0e0a483 - eu-central-1: - PV64: ami-6527cf0a - HVM64: ami-ea26ce85 - HVMG2: ami-d9d62ab6 - ap-northeast-1: - PV64: ami-3e42b65f - HVM64: ami-374db956 - HVMG2: ami-78ba6619 - ap-northeast-2: - PV64: NOT_SUPPORTED - HVM64: ami-2b408b45 - HVMG2: NOT_SUPPORTED - ap-southeast-1: - PV64: ami-df9e4cbc - HVM64: ami-a59b49c6 - HVMG2: ami-56e84c35 - ap-southeast-2: - PV64: ami-63351d00 - HVM64: ami-dc361ebf - HVMG2: ami-2589b946 - ap-south-1: - PV64: NOT_SUPPORTED - HVM64: ami-ffbdd790 - HVMG2: ami-f7354198 - us-east-2: - PV64: NOT_SUPPORTED - HVM64: ami-f6035893 - HVMG2: NOT_SUPPORTED - sa-east-1: - PV64: ami-1ad34676 - HVM64: ami-6dd04501 - HVMG2: NOT_SUPPORTED - cn-north-1: - PV64: ami-77559f1a - HVM64: ami-8e6aa0e3 - HVMG2: NOT_SUPPORTED - Resources: NotificationTopic: Type: AWS::SNS::Topic Properties: + DisplayName: !Sub ${AWS::StackName}-NotificationTopic Subscription: - Endpoint: !Ref OperatorEMail Protocol: email + KmsMasterKeyId: !Ref KmsKeyArn - WebServerGroup: - CreationPolicy: - ResourceSignal: - Timeout: PT15M - Count: 1 - UpdatePolicy: - AutoScalingRollingUpdate: - MinInstancesInService: 1 - MaxBatchSize: 1 - PauseTime: PT15M - WaitOnResourceSignals: true - Type: AWS::AutoScaling::AutoScalingGroup - Properties: - AvailabilityZones: !GetAZs - LaunchConfigurationName: !Ref LaunchConfig - MinSize: "1" - MaxSize: "3" - LoadBalancerNames: - - !Ref ElasticLoadBalancer - NotificationConfigurations: - - TopicARN: !Ref NotificationTopic - NotificationTypes: - - autoscaling:EC2_INSTANCE_LAUNCH - - autoscaling:EC2_INSTANCE_LAUNCH_ERROR - - autoscaling:EC2_INSTANCE_TERMINATE - - autoscaling:EC2_INSTANCE_TERMINATE_ERROR - - LaunchConfig: - Type: AWS::AutoScaling::LaunchConfiguration + LaunchTemplate: + Type: AWS::EC2::LaunchTemplate Metadata: - Comment: Install a simple application AWS::CloudFormation::Init: config: packages: @@ -335,32 +110,20 @@ Resources: owner: root group: root /etc/cfn/cfn-hup.conf: - content: !Join - - "" - - - '[main] ' - - stack= - - !Ref AWS::StackId - - ' ' - - region= - - !Ref AWS::Region - - ' ' + content: !Sub | + [main] + stack=${AWS::StackId} + region=${AWS::Region} mode: "000400" owner: root group: root /etc/cfn/hooks.d/cfn-auto-reloader.conf: - content: !Join - - "" - - - '[cfn-auto-reloader-hook] ' - - 'triggers=post.update ' - - 'path=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init ' - - 'action=/opt/aws/bin/cfn-init -v ' - - ' --stack ' - - !Ref AWS::StackName - - ' --resource LaunchConfig ' - - ' --region ' - - !Ref AWS::Region - - ' ' - - 'runas=root ' + content: !Sub | + [cfn-auto-reloader-hook] + triggers=post.update + path=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init + action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --region ${AWS::Region} + runas=root services: sysvinit: httpd: @@ -373,36 +136,62 @@ Resources: - /etc/cfn/cfn-hup.conf - /etc/cfn/hooks.d/cfn-auto-reloader.conf Properties: - KeyName: !Ref KeyName - ImageId: !FindInMap - - AWSRegionArch2AMI - - !Ref AWS::Region - - !FindInMap - - AWSInstanceType2Arch - - !Ref InstanceType - - Arch - SecurityGroups: - - !Ref InstanceSecurityGroup - InstanceType: !Ref InstanceType - UserData: !Base64 - Fn::Join: - - "" - - - '#!/bin/bash -xe ' - - 'yum update -y aws-cfn-bootstrap ' - - '/opt/aws/bin/cfn-init -v ' - - ' --stack ' - - !Ref AWS::StackName - - ' --resource LaunchConfig ' - - ' --region ' - - !Ref AWS::Region - - ' ' - - '/opt/aws/bin/cfn-signal -e $? ' - - ' --stack ' - - !Ref AWS::StackName - - ' --resource WebServerGroup ' - - ' --region ' - - !Ref AWS::Region - - ' ' + LaunchTemplateName: !Sub ${AWS::StackName}-LaunchTemplate + LaunchTemplateData: + ImageId: !Ref LatestAmiId + InstanceType: !Ref InstanceType + SecurityGroupIds: !Ref SecurityGroups + KeyName: !Ref KeyName + BlockDeviceMappings: + - DeviceName: /dev/sda1 + Ebs: + VolumeSize: 32 + UserData: !Base64 + Fn::Sub: | + #!/bin/bash + /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --region ${AWS::Region} + /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerGroup --region ${AWS::Region} + TagSpecifications: + - ResourceType: instance + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-Instance + + WebServerGroup: + CreationPolicy: + ResourceSignal: + Timeout: PT5M + Count: 1 + UpdatePolicy: + AutoScalingRollingUpdate: + MinInstancesInService: 1 + MaxBatchSize: 1 + PauseTime: PT5M + WaitOnResourceSignals: true + Type: AWS::AutoScaling::AutoScalingGroup + Metadata: + cfn-lint: + config: + ignore_checks: + - E3014 + Properties: + AvailabilityZones: !GetAZs + LaunchTemplate: + LaunchTemplateId: !Ref LaunchTemplate + Version: !GetAtt LaunchTemplate.LatestVersionNumber + MinSize: "1" + MaxSize: "3" + LoadBalancerNames: + - !Ref ElasticLoadBalancer + NotificationConfigurations: + - TopicARN: !Ref NotificationTopic + NotificationTypes: + - autoscaling:EC2_INSTANCE_LAUNCH + - autoscaling:EC2_INSTANCE_LAUNCH_ERROR + - autoscaling:EC2_INSTANCE_TERMINATE + - autoscaling:EC2_INSTANCE_TERMINATE_ERROR + HealthCheckType: ELB + VPCZoneIdentifier: !Ref Subnets WebServerScaleUpPolicy: Type: AWS::AutoScaling::ScalingPolicy @@ -463,6 +252,7 @@ Resources: - LoadBalancerPort: "80" InstancePort: "80" Protocol: HTTP + SSLCertificateId: !Ref CertificateArn HealthCheck: Target: HTTP:80/ HealthyThreshold: "3" @@ -494,5 +284,5 @@ Outputs: Description: The URL of the website Value: !Join - "" - - - http:// + - - https:// - !GetAtt ElasticLoadBalancer.DNSName diff --git a/Config/Config.yaml b/Config/Config.yaml index 043d9666..0aa83b77 100644 --- a/Config/Config.yaml +++ b/Config/Config.yaml @@ -188,7 +188,7 @@ Resources: }); } Handler: index.handler - Runtime: nodejs16.x + Runtime: nodejs20.x Timeout: "30" Role: !GetAtt LambdaExecutionRole.Arn diff --git a/EC2/InstanceWithCfnInit.json b/EC2/InstanceWithCfnInit.json new file mode 100644 index 00000000..c0fdf9e9 --- /dev/null +++ b/EC2/InstanceWithCfnInit.json @@ -0,0 +1,85 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "AWS CloudFormation sample template. \nCreate an Amazon EC2 instance with cfn-init and cfn-signal.\n", + "Resources": { + "Instance": { + "CreationPolicy": { + "ResourceSignal": { + "Timeout": "PT5M" + } + }, + "Type": "AWS::EC2::Instance", + "Metadata": { + "guard": { + "SuppressedRules": [ + "EC2_INSTANCES_IN_VPC" + ] + }, + "AWS::CloudFormation::Init": { + "config": { + "packages": { + "yum": { + "httpd": [] + } + }, + "files": { + "/var/www/html/index.html": { + "content": "\n

Congratulations, you have successfully launched the AWS CloudFormation sample.

\n\n", + "mode": "000644", + "owner": "root", + "group": "root" + }, + "/etc/cfn/cfn-hup.conf": { + "content": { + "Fn::Sub": "[main]\nstack=${AWS::StackId}\nregion=${AWS::Region}\n" + }, + "mode": "000400", + "owner": "root", + "group": "root" + }, + "/etc/cfn/hooks.d/cfn-auto-reloader.conf": { + "content": { + "Fn::Sub": "[cfn-auto-reloader-hook]\ntriggers=post.update\npath=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init\naction=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource Instance --region ${AWS::Region}\nrunas=root" + } + } + }, + "services": { + "sysvinit": { + "httpd": { + "enabled": true, + "ensureRunning": true + }, + "cfn-hup": { + "enabled": true, + "ensureRunning": true, + "files": [ + "/etc/cfn/cfn-hup.conf", + "/etc/cfn/hooks.d/cfn-auto-reloader.conf" + ] + } + } + } + } + } + }, + "Properties": { + "ImageId": "{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-arm64}}", + "InstanceType": "t4g.nano", + "KeyName": "sample", + "BlockDeviceMappings": [ + { + "DeviceName": "/dev/sda1", + "Ebs": { + "VolumeSize": 32 + } + } + ], + "UserData": { + "Fn::Base64": { + "Fn::Sub": "#!/bin/bash\n/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource Instance --region ${AWS::Region}" + } + } + } + } + } +} diff --git a/EC2/InstanceWithCfnInit.yaml b/EC2/InstanceWithCfnInit.yaml new file mode 100644 index 00000000..4378e019 --- /dev/null +++ b/EC2/InstanceWithCfnInit.yaml @@ -0,0 +1,66 @@ +AWSTemplateFormatVersion: "2010-09-09" + +Description: "AWS CloudFormation sample template. \nCreate an Amazon EC2 instance with cfn-init and cfn-signal.\n" + +Resources: + Instance: + CreationPolicy: + ResourceSignal: + Timeout: PT5M + Type: AWS::EC2::Instance + Metadata: + guard: + SuppressedRules: + - EC2_INSTANCES_IN_VPC + AWS::CloudFormation::Init: + config: + packages: + yum: + httpd: [] + files: + /var/www/html/index.html: + content: | + +

Congratulations, you have successfully launched the AWS CloudFormation sample.

+ + mode: "000644" + owner: root + group: root + /etc/cfn/cfn-hup.conf: + content: !Sub | + [main] + stack=${AWS::StackId} + region=${AWS::Region} + mode: "000400" + owner: root + group: root + /etc/cfn/hooks.d/cfn-auto-reloader.conf: + content: !Sub |- + [cfn-auto-reloader-hook] + triggers=post.update + path=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init + action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource Instance --region ${AWS::Region} + runas=root + services: + sysvinit: + httpd: + enabled: true + ensureRunning: true + cfn-hup: + enabled: true + ensureRunning: true + files: + - /etc/cfn/cfn-hup.conf + - /etc/cfn/hooks.d/cfn-auto-reloader.conf + Properties: + ImageId: '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-arm64}}' + InstanceType: t4g.nano + KeyName: sample + BlockDeviceMappings: + - DeviceName: /dev/sda1 + Ebs: + VolumeSize: 32 + UserData: !Base64 + Fn::Sub: |- + #!/bin/bash + /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource Instance --region ${AWS::Region} diff --git a/scripts/guard-all.sh b/scripts/guard-all.sh index 48b5f3f3..eed01750 100755 --- a/scripts/guard-all.sh +++ b/scripts/guard-all.sh @@ -13,5 +13,3 @@ SCRIPT_DIR=$(dirname "$0") find . -name "*.yaml" | grep -v "\.env" | xargs -n 1 ${SCRIPT_DIR}/guard-single.sh - -echo "Success" diff --git a/scripts/guard-single.sh b/scripts/guard-single.sh index 390dae67..fa58062a 100755 --- a/scripts/guard-single.sh +++ b/scripts/guard-single.sh @@ -13,4 +13,3 @@ cfn-guard validate --data $1 \ --show-summary fail \ --type CFNTemplate -echo "Success" diff --git a/scripts/rules.guard b/scripts/rules.guard index e95ed661..5236f1f1 100644 --- a/scripts/rules.guard +++ b/scripts/rules.guard @@ -1693,3 +1693,33 @@ rule IAM_USER_NO_POLICIES_CHECK when %aws_iam_users_no_policies !empty { Fix: Remove the Policies list property from any IAM Users. >> } + +let lambda_functions = Resources.*[ + Type == "AWS::Lambda::Function" +] + +rule LAMBDA_DEPRECATED_RUNTIME when %lambda_functions !empty { + %lambda_functions { + Properties { + when Runtime exists { + Runtime !in ["dotnetcore3.1", "nodejs12.x", "python3.6", "python2.7", "dotnet5.0", "dotnetcore2.1", "ruby2.5", "nodejs10.x", "nodejs8.10", "nodejs4.3", "nodejs6.10", "dotnetcore1.0", "dotnetcore2.0", "nodejs4.3-edge", "nodejs"] + << + Lambda function is using a deprecated runtime. + >> + } + } + } +} + +rule LAMBDA_DEPRECATED_SOON_RUNTIME when %lambda_functions !empty { + %lambda_functions { + Properties { + when Runtime exists { + Runtime !in ["nodejs16.x", "nodejs14.x", "python3.7", "java8", "dotnet7", "go1.x", "ruby2.7", "provided"] + << + Lambda function is using a runtime that is targeted for deprecation. + >> + } + } + } +} diff --git a/scripts/test-all.sh b/scripts/test-all.sh index f5b7ce0b..399cc4f7 100755 --- a/scripts/test-all.sh +++ b/scripts/test-all.sh @@ -48,6 +48,5 @@ then pylint $RCFILE Solutions/ADConnector/src/adconnector_custom_resource.py pylint $RCFILE Solutions/DirectoryServiceSettings/src/directory_settings_custom_resource.py + pylint $RCFILE APIGateway/handler.py fi - -echo "Success" From 53202d4dc1132958938750f7b1ed4bb36bba6dda Mon Sep 17 00:00:00 2001 From: Eric Beard Date: Wed, 15 May 2024 13:25:26 -0700 Subject: [PATCH 2/2] Fix autoscaling sample --- .../AutoScalingMultiAZWithNotifications.json | 118 ++++++++++++------ .../AutoScalingMultiAZWithNotifications.yaml | 96 +++++++++----- scripts/format-yaml-single.sh | 2 +- 3 files changed, 149 insertions(+), 67 deletions(-) diff --git a/AutoScaling/AutoScalingMultiAZWithNotifications.json b/AutoScaling/AutoScalingMultiAZWithNotifications.json index 6d521c1c..6b4ceda5 100644 --- a/AutoScaling/AutoScalingMultiAZWithNotifications.json +++ b/AutoScaling/AutoScalingMultiAZWithNotifications.json @@ -1,17 +1,15 @@ { "AWSTemplateFormatVersion": "2010-09-09", - "Description": "AWS CloudFormation Sample Template AutoScalingMultiAZWithNotifications: Create a multi-az, load balanced and Auto Scaled sample web site running on an Apache Web Server. The application is configured to span all Availability Zones in the region and is Auto-Scaled based on the CPU utilization of the web servers. Notifications will be sent to the operator email address on scaling events. The instances are load balanced with a simple health check against the default web page. **WARNING** This template creates one or more Amazon EC2 instances and an Elastic Load Balancer. You will be billed for the AWS resources used if you create a stack from this template.", + "Description": "Create a multi-az, load balanced and Auto Scaled sample web site running on\nan Apache Web Server. The application is configured to span all\nAvailability Zones in the region and is Auto-Scaled based on the CPU\nutilization of the web servers. Notifications will be sent to the operator\nemail address on scaling events. The instances are load balanced with a\nsimple health check against the default web page. **WARNING** This template\ncreates one or more Amazon EC2 instances and an Elastic Load Balancer. You\nwill be billed for the AWS resources used if you create a stack from this\ntemplate.\n", "Parameters": { "InstanceType": { "Description": "WebServer EC2 instance type", "Type": "String", - "Default": "t2.micro" + "Default": "t4g.micro" }, "OperatorEMail": { - "Description": "EMail address to notify if there are any scaling operations", - "Type": "String", - "AllowedPattern": "([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)", - "ConstraintDescription": "must be a valid email address." + "Description": "Email address to notify if there are any scaling operations", + "Type": "String" }, "KeyName": { "Description": "The EC2 Key Pair to allow SSH access to the instances", @@ -46,6 +44,14 @@ "Subnets": { "Description": "Subnets to be used", "Type": "List" + }, + "AZs": { + "Description": "Availability Zones to be used", + "Type": "List" + }, + "VPC": { + "Description": "VPC to be used", + "Type": "AWS::EC2::VPC::Id" } }, "Mappings": { @@ -156,7 +162,7 @@ }, "/etc/cfn/hooks.d/cfn-auto-reloader.conf": { "content": { - "Fn::Sub": "[cfn-auto-reloader-hook]\ntriggers=post.update\npath=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init\naction=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --region ${AWS::Region}\nrunas=root\n" + "Fn::Sub": "[cfn-auto-reloader-hook]\ntriggers=post.update\npath=Resources.LaunchTemplate.Metadata.AWS::CloudFormation::Init\naction=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchTemplate --region ${AWS::Region}\nrunas=root\n" } } }, @@ -206,7 +212,7 @@ ], "UserData": { "Fn::Base64": { - "Fn::Sub": "#!/bin/bash\n/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --region ${AWS::Region}\n/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerGroup --region ${AWS::Region}\n" + "Fn::Sub": "#!/bin/bash\n/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchTemplate --region ${AWS::Region}\n/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerGroup --region ${AWS::Region}\n" } }, "TagSpecifications": [ @@ -252,7 +258,7 @@ }, "Properties": { "AvailabilityZones": { - "Fn::GetAZs": null + "Ref": "AZs" }, "LaunchTemplate": { "LaunchTemplateId": { @@ -267,9 +273,9 @@ }, "MinSize": "1", "MaxSize": "3", - "LoadBalancerNames": [ + "TargetGroupARNs": [ { - "Ref": "ElasticLoadBalancer" + "Ref": "TargetGroup" } ], "NotificationConfigurations": [ @@ -366,28 +372,73 @@ } }, "ElasticLoadBalancer": { - "Type": "AWS::ElasticLoadBalancing::LoadBalancer", + "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", "Properties": { - "AvailabilityZones": { - "Fn::GetAZs": null + "Scheme": "internet-facing", + "SecurityGroups": [ + { + "Ref": "LoadBalancerSecurityGroup" + } + ], + "Subnets": { + "Ref": "Subnets" }, - "CrossZone": true, - "Listeners": [ + "Type": "application" + } + }, + "LoadBalancerSecurityGroup": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Allows inbound traffic on port 443", + "SecurityGroupIngress": [ { - "LoadBalancerPort": "80", - "InstancePort": "80", - "Protocol": "HTTP", - "SSLCertificateId": { - "Ref": "CertificateArn" + "IpProtocol": "tcp", + "FromPort": 443, + "ToPort": 443, + "CidrIp": "0.0.0.0/0" + } + ], + "VpcId": { + "Ref": "VPC" + } + } + }, + "LoadBalancerListener": { + "Type": "AWS::ElasticLoadBalancingV2::Listener", + "Properties": { + "DefaultActions": [ + { + "Type": "forward", + "TargetGroupArn": { + "Ref": "TargetGroup" } } ], - "HealthCheck": { - "Target": "HTTP:80/", - "HealthyThreshold": "3", - "UnhealthyThreshold": "5", - "Interval": "30", - "Timeout": "5" + "LoadBalancerArn": { + "Ref": "ElasticLoadBalancer" + }, + "Port": 443, + "Protocol": "HTTPS", + "SslPolicy": "ELBSecurityPolicy-2016-08", + "Certificates": [ + { + "CertificateArn": { + "Ref": "CertificateArn" + } + } + ] + } + }, + "TargetGroup": { + "Type": "AWS::ElasticLoadBalancingV2::TargetGroup", + "Properties": { + "HealthCheckPath": "/", + "Name": "MyTargetGroup", + "Port": 80, + "Protocol": "HTTP", + "TargetType": "instance", + "VpcId": { + "Ref": "VPC" } } }, @@ -415,17 +466,8 @@ "IpProtocol": "tcp", "FromPort": 80, "ToPort": 80, - "SourceSecurityGroupOwnerId": { - "Fn::GetAtt": [ - "ElasticLoadBalancer", - "SourceSecurityGroup.OwnerAlias" - ] - }, - "SourceSecurityGroupName": { - "Fn::GetAtt": [ - "ElasticLoadBalancer", - "SourceSecurityGroup.GroupName" - ] + "SourceSecurityGroupId": { + "Ref": "LoadBalancerSecurityGroup" } } ] diff --git a/AutoScaling/AutoScalingMultiAZWithNotifications.yaml b/AutoScaling/AutoScalingMultiAZWithNotifications.yaml index e14f32f5..e4f28bb1 100644 --- a/AutoScaling/AutoScalingMultiAZWithNotifications.yaml +++ b/AutoScaling/AutoScalingMultiAZWithNotifications.yaml @@ -1,18 +1,25 @@ AWSTemplateFormatVersion: "2010-09-09" -Description: 'AWS CloudFormation Sample Template AutoScalingMultiAZWithNotifications: Create a multi-az, load balanced and Auto Scaled sample web site running on an Apache Web Server. The application is configured to span all Availability Zones in the region and is Auto-Scaled based on the CPU utilization of the web servers. Notifications will be sent to the operator email address on scaling events. The instances are load balanced with a simple health check against the default web page. **WARNING** This template creates one or more Amazon EC2 instances and an Elastic Load Balancer. You will be billed for the AWS resources used if you create a stack from this template.' +Description: | + Create a multi-az, load balanced and Auto Scaled sample web site running on + an Apache Web Server. The application is configured to span all + Availability Zones in the region and is Auto-Scaled based on the CPU + utilization of the web servers. Notifications will be sent to the operator + email address on scaling events. The instances are load balanced with a + simple health check against the default web page. **WARNING** This template + creates one or more Amazon EC2 instances and an Elastic Load Balancer. You + will be billed for the AWS resources used if you create a stack from this + template. Parameters: InstanceType: Description: WebServer EC2 instance type Type: String - Default: t2.micro + Default: t4g.micro OperatorEMail: - Description: EMail address to notify if there are any scaling operations + Description: Email address to notify if there are any scaling operations Type: String - AllowedPattern: ([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?) - ConstraintDescription: must be a valid email address. KeyName: Description: The EC2 Key Pair to allow SSH access to the instances @@ -48,6 +55,14 @@ Parameters: Description: Subnets to be used Type: List + AZs: + Description: Availability Zones to be used + Type: List + + VPC: + Description: VPC to be used + Type: AWS::EC2::VPC::Id + Mappings: Region2Examples: us-east-1: @@ -121,8 +136,8 @@ Resources: content: !Sub | [cfn-auto-reloader-hook] triggers=post.update - path=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init - action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --region ${AWS::Region} + path=Resources.LaunchTemplate.Metadata.AWS::CloudFormation::Init + action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchTemplate --region ${AWS::Region} runas=root services: sysvinit: @@ -149,7 +164,7 @@ Resources: UserData: !Base64 Fn::Sub: | #!/bin/bash - /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --region ${AWS::Region} + /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchTemplate --region ${AWS::Region} /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerGroup --region ${AWS::Region} TagSpecifications: - ResourceType: instance @@ -158,6 +173,7 @@ Resources: Value: !Sub ${AWS::StackName}-Instance WebServerGroup: + Type: AWS::AutoScaling::AutoScalingGroup CreationPolicy: ResourceSignal: Timeout: PT5M @@ -168,21 +184,20 @@ Resources: MaxBatchSize: 1 PauseTime: PT5M WaitOnResourceSignals: true - Type: AWS::AutoScaling::AutoScalingGroup Metadata: cfn-lint: config: ignore_checks: - E3014 Properties: - AvailabilityZones: !GetAZs + AvailabilityZones: !Ref AZs LaunchTemplate: LaunchTemplateId: !Ref LaunchTemplate Version: !GetAtt LaunchTemplate.LatestVersionNumber MinSize: "1" MaxSize: "3" - LoadBalancerNames: - - !Ref ElasticLoadBalancer + TargetGroupARNs: + - !Ref TargetGroup NotificationConfigurations: - TopicARN: !Ref NotificationTopic NotificationTypes: @@ -244,21 +259,47 @@ Resources: ComparisonOperator: LessThanThreshold ElasticLoadBalancer: - Type: AWS::ElasticLoadBalancing::LoadBalancer + Type: AWS::ElasticLoadBalancingV2::LoadBalancer + Properties: + Scheme: internet-facing + SecurityGroups: + - !Ref LoadBalancerSecurityGroup + Subnets: !Ref Subnets + Type: application + + LoadBalancerSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Allows inbound traffic on port 443 + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 443 + ToPort: 443 + CidrIp: 0.0.0.0/0 + VpcId: !Ref VPC + + LoadBalancerListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + DefaultActions: + - Type: forward + TargetGroupArn: !Ref TargetGroup + LoadBalancerArn: !Ref ElasticLoadBalancer + Port: 443 + Protocol: HTTPS + SslPolicy: ELBSecurityPolicy-2016-08 + Certificates: + - CertificateArn: !Ref CertificateArn + + TargetGroup: + Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: - AvailabilityZones: !GetAZs - CrossZone: true - Listeners: - - LoadBalancerPort: "80" - InstancePort: "80" - Protocol: HTTP - SSLCertificateId: !Ref CertificateArn - HealthCheck: - Target: HTTP:80/ - HealthyThreshold: "3" - UnhealthyThreshold: "5" - Interval: "30" - Timeout: "5" + HealthCheckPath: / + Name: MyTargetGroup + Port: 80 + Protocol: HTTP + TargetType: instance + VpcId: !Ref VPC InstanceSecurityGroup: Type: AWS::EC2::SecurityGroup @@ -276,8 +317,7 @@ Resources: - IpProtocol: tcp FromPort: 80 ToPort: 80 - SourceSecurityGroupOwnerId: !GetAtt ElasticLoadBalancer.SourceSecurityGroup.OwnerAlias - SourceSecurityGroupName: !GetAtt ElasticLoadBalancer.SourceSecurityGroup.GroupName + SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup Outputs: URL: diff --git a/scripts/format-yaml-single.sh b/scripts/format-yaml-single.sh index b7c12fb3..8079f6be 100755 --- a/scripts/format-yaml-single.sh +++ b/scripts/format-yaml-single.sh @@ -2,5 +2,5 @@ set -eou pipefail echo $1 n=$(basename $1) -rain fmt $1 > /tmp/$n +rain fmt -u $1 > /tmp/$n mv /tmp/$n $1