AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: >
"Sample solution to deploy a sample website on EC2 instances with HTTPS/TLS
functions performed by Nitro Enclaves"
Metadata:
License:
Description: >
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: MIT-0
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Parameters:
EnvironmentName:
Type: String
Default: "Nitro Enclaves ACM"
SampleAutoScalingGroupInstanceType:
Type: String
Default: "c5.xlarge"
Description: >
Instance type for sample Auto Scaling group. Must be a Virtualized Nitro-based
instance type with at least four vCPUs, except t3, t3a, t4g, a1, c6g, c6gd, m6g,
m6gd, r6g, and r6gd.
HostedZone:
Type: AWS::Route53::HostedZone::Id
Description: A Route53 Hosted Zone to use for issuing a certificate.
DomainName:
Type: String
Description: >
FQDN for the sample to be deployed at. This should be the zone apex
or a subdomain of your specified hosted zone.
SSMConfig:
AllowedValues:
- "true"
- "false"
Default: "false"
Description: >
Enable SSM-Agent on your EC2 instances and provide instance profile
permissions required for SSM Quick Setup. Permits SSM Management
Functions include Systems Manager Session Manager for remote command
line access to EC2 instances
Type: String
Mappings:
Region2Examples:
us-east-1:
Examples: 'https://s3.amazonaws.com/cloudformation-examples-us-east-1'
us-west-2:
Examples: 'https://s3-us-west-2.amazonaws.com/cloudformation-examples-us-west-2'
us-west-1:
Examples: 'https://s3-us-west-1.amazonaws.com/cloudformation-examples-us-west-1'
eu-west-1:
Examples: 'https://s3-eu-west-1.amazonaws.com/cloudformation-examples-eu-west-1'
eu-west-2:
Examples: 'https://s3-eu-west-2.amazonaws.com/cloudformation-examples-eu-west-2'
eu-west-3:
Examples: 'https://s3-eu-west-3.amazonaws.com/cloudformation-examples-eu-west-3'
eu-north-1:
Examples: 'https://s3-eu-north-1.amazonaws.com/cloudformation-examples-eu-north-1'
eu-central-1:
Examples: >-
https://s3-eu-central-1.amazonaws.com/cloudformation-examples-eu-central-1
ap-southeast-1:
Examples: >-
https://s3-ap-southeast-1.amazonaws.com/cloudformation-examples-ap-southeast-1
ap-northeast-1:
Examples: >-
https://s3-ap-northeast-1.amazonaws.com/cloudformation-examples-ap-northeast-1
ap-southeast-2:
Examples: >-
https://s3-ap-southeast-2.amazonaws.com/cloudformation-examples-ap-southeast-2
ap-south-1:
Examples: 'https://s3-ap-south-1.amazonaws.com/cloudformation-examples-ap-south-1'
us-east-2:
Examples: 'https://s3-us-east-2.amazonaws.com/cloudformation-examples-us-east-2'
sa-east-1:
Examples: 'https://s3-sa-east-1.amazonaws.com/cloudformation-examples-sa-east-1'
Region2AMI:
us-east-1:
AMI: 'ami-09a1f053468bd3dcf'
us-east-2:
AMI: 'ami-0bc56d1a0e5584cb4'
us-west-2:
AMI: 'ami-0f0124ebc1f49daf5'
ap-east-1:
AMI: 'ami-09b985981f1c12e8f'
ap-northeast-1:
AMI: 'ami-0022e5e5ce88aa323'
ap-south-1:
AMI: 'ami-042fc7f2cda314ab4'
ap-southeast-1:
AMI: 'ami-0ed46cdbe7ca952fc'
ap-southeast-2:
AMI: 'ami-005448c3d2eab01b5'
eu-central-1:
AMI: 'ami-0acdbcca812cefd03'
eu-north-1:
AMI: 'ami-031805abef5291506'
eu-west-1:
AMI: 'ami-09be95e78685c4142'
eu-west-2:
AMI: 'ami-06ddc10f4b58d3f0b'
eu-west-3:
AMI: 'ami-00da9426142772b8a'
sa-east-1:
AMI: 'ami-0b2022ccbbf6bb599'
Conditions:
SSMEnable: !Equals [ !Ref SSMConfig , 'true' ]
Resources:
InstanceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
NitroEnclavesACMPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: NitroEnclavesACMPolicy
Roles:
- !Ref InstanceRole
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:GetObject
Resource:
- !Sub arn:aws:s3:::${AssocEnclaveCert.CertificateS3BucketName}/*
- Effect: Allow
Action:
- kms:Decrypt
Resource: !Sub arn:aws:kms:${AWS::Region}:*:key/${AssocEnclaveCert.EncryptionKmsKeyId}
SSMPolicy:
Type: AWS::IAM::Policy
Condition: SSMEnable
Properties:
PolicyName: SSMQuickStartPolicy
Roles:
- !Ref InstanceRole
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ssm:DescribeAssociation
- ssm:GetDeployablePatchSnapshotForInstance
- ssm:GetDocument
- ssm:DescribeDocument
- ssm:GetManifest
- ssm:GetParameter
- ssm:GetParameters
- ssm:ListAssociations
- ssm:ListInstanceAssociations
- ssm:PutInventory
- ssm:PutComplianceItems
- ssm:PutConfigurePackageResult
- ssm:UpdateAssociationStatus
- ssm:UpdateInstanceAssociationStatus
- ssm:UpdateInstanceInformation
Resource: '*'
- Effect: Allow
Action:
- ssmmessages:CreateControlChannel
- ssmmessages:CreateDataChannel
- ssmmessages:OpenControlChannel
- ssmmessages:OpenDataChannel
Resource: '*'
- Effect: Allow
Action:
- ec2messages:AcknowledgeMessage
- ec2messages:DeleteMessage
- ec2messages:FailMessage
- ec2messages:GetEndpoint
- ec2messages:GetMessages
- ec2messages:SendReply
Resource: '*'
AssocEnclaveCert:
Type: Custom::AssocEnclaveCert
Properties:
ServiceToken: !GetAtt AssocEnclaveCertFunction.Arn
Region: !Ref "AWS::Region"
InstanceRole: !Ref InstanceRole
InstanceRoleArn: !GetAtt InstanceRole.Arn
HostedZoneID: !Ref HostedZone
DomainName: !Ref route53RS
CertificateARN: !Ref ACMCert
AssocEnclaveCertFunction:
Type: 'AWS::Serverless::Function'
Properties:
Handler: AssocEnclaveCertFunction.handler
Runtime: nodejs12.x
Timeout: 30
Role: !GetAtt AssocEnclaveCertLambdaRole.Arn
CodeUri: AssocEnclaveCertFunction/
AssocEnclaveCertLambdaRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: "/service-role/"
Policies:
- PolicyName: lambdaExecution-AssocEnclaveCert
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
Resource: '*'
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource: '*'
- Effect: Allow
Action:
- ec2:AssociateEnclaveCertificateIamRole
- ec2:GetAssociatedEnclaveCertificateIamRoles
- ec2:DisassociateEnclaveCertificateIamRole
Resource:
- !Ref ACMCert
- !GetAtt [ InstanceRole, Arn ]
route53RS:
Type: AWS::Route53::RecordSet
Properties:
Name: !Ref DomainName
Comment: NitroEnclaveSample
Type: A
AliasTarget:
DNSName: !GetAtt NetworkLoadBalancer.DNSName
HostedZoneId: !GetAtt NetworkLoadBalancer.CanonicalHostedZoneID
HostedZoneId: !Ref HostedZone
EnclavesIAMInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref InstanceRole
ACMCert:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: !Ref route53RS
DomainValidationOptions:
- DomainName: !Ref route53RS
HostedZoneId: !Ref HostedZone
ValidationMethod: DNS
SampleAutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
DependsOn: AssocEnclaveCert
CreationPolicy:
AutoScalingCreationPolicy:
MinSuccessfulInstancesPercent: 100
ResourceSignal:
Timeout: PT15M
Count: 1
UpdatePolicy:
AutoScalingReplacingUpdate:
WillReplace: true
Properties:
MinSize: "2"
MaxSize: "4"
DesiredCapacity: "2"
TargetGroupARNs:
- !Ref NLBTargetGroup
VPCZoneIdentifier:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
LaunchTemplate:
LaunchTemplateId: !Ref SampleLaunchTemplate
Version: !GetAtt SampleLaunchTemplate.LatestVersionNumber
Tags:
- Key: Name
Value: !Ref EnvironmentName
PropagateAtLaunch: true
SampleLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateData:
IamInstanceProfile:
Arn: !GetAtt EnclavesIAMInstanceProfile.Arn
ImageId: !FindInMap
- Region2AMI
- !Ref 'AWS::Region'
- AMI
InstanceType: !Ref SampleAutoScalingGroupInstanceType
SecurityGroupIds:
- !GetAtt VPC.DefaultSecurityGroup
- !Ref SecurityGroup
EnclaveOptions:
Enabled: true
UserData: !Base64
'Fn::Join':
- ''
- - |
#!/bin/bash -xe
- |
- '/opt/aws/bin/cfn-init -v '
- '-c install'
- ' --stack '
- !Ref 'AWS::StackName'
- ' --resource SampleLaunchTemplate '
- ' --region '
- !Ref 'AWS::Region'
- |+
- '/opt/aws/bin/cfn-signal -e $? '
- ' --stack '
- !Ref 'AWS::StackName'
- ' --resource SampleAutoScalingGroup '
- ' --region '
- !Ref 'AWS::Region'
- |+
Metadata:
Comment: Install a simple application
'AWS::CloudFormation::Init':
configSets:
!If
- SSMEnable
- install:
- install_and_enable_cfn_hup
- config1
- config2
- config3
- SSM
- install:
- install_and_enable_cfn_hup
- config1
- config2
- config3
install_and_enable_cfn_hup:
files:
/etc/cfn/cfn-hup.conf:
content: !Sub |
[main]
stack=${AWS::StackId}
region=${AWS::Region}
verbose=true
interval=5
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.EC2.Metadata.AWS::CloudFormation::Init
action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource SampleLaunchTemplate --configsets install --region ${AWS::Region}
mode: "000400"
owner: root
group: root
/lib/systemd/system/cfn-hup.service:
content: |
[Unit]
Description=cfn-hup daemon
[Service]
Type=simple
ExecStart=/opt/aws/bin/cfn-hup
Restart=always
[Install]
WantedBy=multi-user.target
mode: "000400"
owner: root
group: root
commands:
01enable_cfn_hup:
command: systemctl enable cfn-hup.service
02start_cfn_hup:
command: systemctl start cfn-hup.service
config1:
files:
/usr/share/nginx/html/index.html:
content: !Sub
- >-