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..6b4ceda5 100644 --- a/AutoScaling/AutoScalingMultiAZWithNotifications.json +++ b/AutoScaling/AutoScalingMultiAZWithNotifications.json @@ -1,75 +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.", - "Metadata": { - "License": "Apache-2.0" - }, + "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", - "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": "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", @@ -84,6 +24,34 @@ "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" + }, + "AZs": { + "Description": "Availability Zones to be used", + "Type": "List" + }, + "VPC": { + "Description": "VPC to be used", + "Type": "AWS::EC2::VPC::Id" } }, "Mappings": { @@ -127,240 +95,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 +111,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 +154,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 +162,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.LaunchTemplate.Metadata.AWS::CloudFormation::Init\naction=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchTemplate --region ${AWS::Region}\nrunas=root\n" } } }, @@ -520,66 +186,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 LaunchTemplate --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": { + "Ref": "AZs" }, - "SecurityGroups": [ + "LaunchTemplate": { + "LaunchTemplateId": { + "Ref": "LaunchTemplate" + }, + "Version": { + "Fn::GetAtt": [ + "LaunchTemplate", + "LatestVersionNumber" + ] + } + }, + "MinSize": "1", + "MaxSize": "3", + "TargetGroupARNs": [ { - "Ref": "InstanceSecurityGroup" + "Ref": "TargetGroup" } ], - "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" } } }, @@ -658,25 +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" + "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" } } }, @@ -704,17 +466,8 @@ "IpProtocol": "tcp", "FromPort": 80, "ToPort": 80, - "SourceSecurityGroupOwnerId": { - "Fn::GetAtt": [ - "ElasticLoadBalancer", - "SourceSecurityGroup.OwnerAlias" - ] - }, - "SourceSecurityGroupName": { - "Fn::GetAtt": [ - "ElasticLoadBalancer", - "SourceSecurityGroup.GroupName" - ] + "SourceSecurityGroupId": { + "Ref": "LoadBalancerSecurityGroup" } } ] @@ -728,7 +481,7 @@ "Fn::Join": [ "", [ - "http://", + "https://", { "Fn::GetAtt": [ "ElasticLoadBalancer", diff --git a/AutoScaling/AutoScalingMultiAZWithNotifications.yaml b/AutoScaling/AutoScalingMultiAZWithNotifications.yaml index 557fba8f..e4f28bb1 100644 --- a/AutoScaling/AutoScalingMultiAZWithNotifications.yaml +++ b/AutoScaling/AutoScalingMultiAZWithNotifications.yaml @@ -1,75 +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.' - -Metadata: - License: Apache-2.0 +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 - 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: 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 @@ -85,6 +35,34 @@ 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 + + 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: @@ -114,207 +92,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 +125,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.LaunchTemplate.Metadata.AWS::CloudFormation::Init + action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchTemplate --region ${AWS::Region} + runas=root services: sysvinit: httpd: @@ -373,36 +151,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 LaunchTemplate --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: + Type: AWS::AutoScaling::AutoScalingGroup + CreationPolicy: + ResourceSignal: + Timeout: PT5M + Count: 1 + UpdatePolicy: + AutoScalingRollingUpdate: + MinInstancesInService: 1 + MaxBatchSize: 1 + PauseTime: PT5M + WaitOnResourceSignals: true + Metadata: + cfn-lint: + config: + ignore_checks: + - E3014 + Properties: + AvailabilityZones: !Ref AZs + LaunchTemplate: + LaunchTemplateId: !Ref LaunchTemplate + Version: !GetAtt LaunchTemplate.LatestVersionNumber + MinSize: "1" + MaxSize: "3" + TargetGroupARNs: + - !Ref TargetGroup + 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 @@ -455,20 +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 - 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 @@ -486,13 +317,12 @@ Resources: - IpProtocol: tcp FromPort: 80 ToPort: 80 - SourceSecurityGroupOwnerId: !GetAtt ElasticLoadBalancer.SourceSecurityGroup.OwnerAlias - SourceSecurityGroupName: !GetAtt ElasticLoadBalancer.SourceSecurityGroup.GroupName + SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup Outputs: URL: 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/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 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"