Skip to content

Instantly share code, notes, and snippets.

@jarrodmedrano
Created April 12, 2023 19:15
Show Gist options
  • Save jarrodmedrano/8413f86fc3d7526eff8eaa4dea64903e to your computer and use it in GitHub Desktop.
Save jarrodmedrano/8413f86fc3d7526eff8eaa4dea64903e to your computer and use it in GitHub Desktop.
VPC EFS Aurora example
Description: Animals4Life base VPC Template + EFS + Aurora DB Cluster
Parameters:
LatestAmiId:
Description: AMI for Wordpess Instance (default is latest AmaLinux2)
Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
DBName:
AllowedPattern: '[a-zA-Z][a-zA-Z0-9]*'
ConstraintDescription: must begin with a letter and contain only alphanumeric characters.
Default: 'a4lwordpress'
Description: The WordPress database name
MaxLength: '64'
MinLength: '1'
Type: String
DBPassword:
ConstraintDescription: must contain only alphanumeric characters.
Description: The WordPress database admin account password
MaxLength: '41'
MinLength: '8'
Type: String
Default: '4n1m4ls4L1f3'
DBRootPassword:
ConstraintDescription: must contain only alphanumeric characters.
Description: MySQL root password
MaxLength: '41'
MinLength: '8'
Type: String
Default: '4n1m4ls4L1f3'
DBUser:
ConstraintDescription: must begin with a letter and contain only alphanumeric characters.
Description: The WordPress database admin account username
Default: 'a4lwordpress'
MaxLength: '16'
MinLength: '1'
Type: String
DBInstanceType:
Type: String
Default: 'db.t3.medium'
DatabaseRestoreSnapshot:
Description: The snapshot name to restore from - Leave Blank for a new DB, USE ARN for a MIGRATION of a non aurora snapshot or name for aurora
Type: String
DBVersion:
Description: 'The version of Aurora to use (this can cause compatibility impacts - be certain !'
Default: "8.0.mysql_aurora.3.02.0"
Type: String
Conditions:
NoSnapshot:
!Equals ['', !Ref DatabaseRestoreSnapshot]
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.16.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: a4l-vpc1
IPv6CidrBlock:
Type: AWS::EC2::VPCCidrBlock
Properties:
VpcId: !Ref VPC
AmazonProvidedIpv6CidrBlock: true
InternetGateway:
Type: 'AWS::EC2::InternetGateway'
Properties:
Tags:
- Key: Name
Value: A4L-vpc1-igw
InternetGatewayAttachment:
Type: 'AWS::EC2::VPCGatewayAttachment'
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
RouteTableWeb:
Type: 'AWS::EC2::RouteTable'
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: A4L-vpc1-rt-web
RouteTableWebDefaultIPv4:
Type: 'AWS::EC2::Route'
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId:
Ref: RouteTableWeb
DestinationCidrBlock: '0.0.0.0/0'
GatewayId:
Ref: InternetGateway
RouteTableWebDefaultIPv6:
Type: 'AWS::EC2::Route'
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId:
Ref: RouteTableWeb
DestinationIpv6CidrBlock: '::/0'
GatewayId:
Ref: InternetGateway
RouteTableAssociationWebA:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
Properties:
SubnetId: !Ref SubnetWEBA
RouteTableId:
Ref: RouteTableWeb
RouteTableAssociationWebB:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
Properties:
SubnetId: !Ref SubnetWEBB
RouteTableId:
Ref: RouteTableWeb
RouteTableAssociationWebC:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
Properties:
SubnetId: !Ref SubnetWEBC
RouteTableId:
Ref: RouteTableWeb
SubnetReservedA:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: 10.16.0.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock:
Fn::Sub:
- "${VpcPart}${SubnetPart}"
- SubnetPart: '00::/64'
VpcPart: !Select [ 0, !Split [ '00::/56', !Select [ 0, !GetAtt VPC.Ipv6CidrBlocks ]]]
Tags:
- Key: Name
Value: sn-reserved-A
SubnetReservedB:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 1, !GetAZs '' ]
CidrBlock: 10.16.64.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock:
Fn::Sub:
- "${VpcPart}${SubnetPart}"
- SubnetPart: '04::/64'
VpcPart: !Select [ 0, !Split [ '00::/56', !Select [ 0, !GetAtt VPC.Ipv6CidrBlocks ]]]
Tags:
- Key: Name
Value: sn-reserved-B
SubnetReservedC:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 2, !GetAZs '' ]
CidrBlock: 10.16.128.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock:
Fn::Sub:
- "${VpcPart}${SubnetPart}"
- SubnetPart: '08::/64'
VpcPart: !Select [ 0, !Split [ '00::/56', !Select [ 0, !GetAtt VPC.Ipv6CidrBlocks ]]]
Tags:
- Key: Name
Value: sn-reserved-C
SubnetDBA:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: 10.16.16.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock:
Fn::Sub:
- "${VpcPart}${SubnetPart}"
- SubnetPart: '01::/64'
VpcPart: !Select [ 0, !Split [ '00::/56', !Select [ 0, !GetAtt VPC.Ipv6CidrBlocks ]]]
Tags:
- Key: Name
Value: sn-db-A
SubnetDBB:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 1, !GetAZs '' ]
CidrBlock: 10.16.80.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock:
Fn::Sub:
- "${VpcPart}${SubnetPart}"
- SubnetPart: '05::/64'
VpcPart: !Select [ 0, !Split [ '00::/56', !Select [ 0, !GetAtt VPC.Ipv6CidrBlocks ]]]
Tags:
- Key: Name
Value: sn-db-B
SubnetDBC:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 2, !GetAZs '' ]
CidrBlock: 10.16.144.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock:
Fn::Sub:
- "${VpcPart}${SubnetPart}"
- SubnetPart: '09::/64'
VpcPart: !Select [ 0, !Split [ '00::/56', !Select [ 0, !GetAtt VPC.Ipv6CidrBlocks ]]]
Tags:
- Key: Name
Value: sn-db-C
SubnetAPPA:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: 10.16.32.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock:
Fn::Sub:
- "${VpcPart}${SubnetPart}"
- SubnetPart: '02::/64'
VpcPart: !Select [ 0, !Split [ '00::/56', !Select [ 0, !GetAtt VPC.Ipv6CidrBlocks ]]]
Tags:
- Key: Name
Value: sn-app-A
SubnetAPPB:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 1, !GetAZs '' ]
CidrBlock: 10.16.96.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock:
Fn::Sub:
- "${VpcPart}${SubnetPart}"
- SubnetPart: '06::/64'
VpcPart: !Select [ 0, !Split [ '00::/56', !Select [ 0, !GetAtt VPC.Ipv6CidrBlocks ]]]
Tags:
- Key: Name
Value: sn-app-B
SubnetAPPC:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 2, !GetAZs '' ]
CidrBlock: 10.16.160.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock:
Fn::Sub:
- "${VpcPart}${SubnetPart}"
- SubnetPart: '0A::/64'
VpcPart: !Select [ 0, !Split [ '00::/56', !Select [ 0, !GetAtt VPC.Ipv6CidrBlocks ]]]
Tags:
- Key: Name
Value: sn-app-C
SubnetWEBA:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: 10.16.48.0/20
MapPublicIpOnLaunch: true
Ipv6CidrBlock:
Fn::Sub:
- "${VpcPart}${SubnetPart}"
- SubnetPart: '03::/64'
VpcPart: !Select [ 0, !Split [ '00::/56', !Select [ 0, !GetAtt VPC.Ipv6CidrBlocks ]]]
Tags:
- Key: Name
Value: sn-web-A
SubnetWEBB:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 1, !GetAZs '' ]
CidrBlock: 10.16.112.0/20
MapPublicIpOnLaunch: true
Ipv6CidrBlock:
Fn::Sub:
- "${VpcPart}${SubnetPart}"
- SubnetPart: '07::/64'
VpcPart: !Select [ 0, !Split [ '00::/56', !Select [ 0, !GetAtt VPC.Ipv6CidrBlocks ]]]
Tags:
- Key: Name
Value: sn-web-B
SubnetWEBC:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 2, !GetAZs '' ]
CidrBlock: 10.16.176.0/20
MapPublicIpOnLaunch: true
Ipv6CidrBlock:
Fn::Sub:
- "${VpcPart}${SubnetPart}"
- SubnetPart: '0B::/64'
VpcPart: !Select [ 0, !Split [ '00::/56', !Select [ 0, !GetAtt VPC.Ipv6CidrBlocks ]]]
Tags:
- Key: Name
Value: sn-web-C
IPv6WorkaroundSubnetWEBA:
Type: Custom::SubnetModify
Properties:
ServiceToken: !GetAtt IPv6WorkaroundLambda.Arn
SubnetId: !Ref SubnetWEBA
IPv6WorkaroundSubnetWEBB:
Type: Custom::SubnetModify
Properties:
ServiceToken: !GetAtt IPv6WorkaroundLambda.Arn
SubnetId: !Ref SubnetWEBB
IPv6WorkaroundSubnetWEBC:
Type: Custom::SubnetModify
Properties:
ServiceToken: !GetAtt IPv6WorkaroundLambda.Arn
SubnetId: !Ref SubnetWEBC
IPv6WorkaroundRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
Policies:
- PolicyName: !Sub "ipv6-fix-logs-${AWS::StackName}"
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
- PolicyName: !Sub "ipv6-fix-modify-${AWS::StackName}"
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ec2:ModifySubnetAttribute
Resource: "*"
IPv6WorkaroundLambda:
Type: AWS::Lambda::Function
Properties:
Handler: "index.lambda_handler"
Code: #import cfnresponse below required to send respose back to CFN
ZipFile:
Fn::Sub: |
import cfnresponse
import boto3
def lambda_handler(event, context):
if event['RequestType'] is 'Delete':
cfnresponse.send(event, context, cfnresponse.SUCCESS)
return
responseValue = event['ResourceProperties']['SubnetId']
ec2 = boto3.client('ec2', region_name='${AWS::Region}')
ec2.modify_subnet_attribute(AssignIpv6AddressOnCreation={
'Value': True
},
SubnetId=responseValue)
responseData = {}
responseData['SubnetId'] = responseValue
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")
Runtime: python3.9
Role: !GetAtt IPv6WorkaroundRole.Arn
Timeout: 30
RDSSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref VPC
GroupDescription: "Ingress control for RDS instance"
SecurityGroupIngress:
- Description: 'Allow MySQL IPv4 IN'
IpProtocol: tcp
FromPort: '3306'
ToPort: '3306'
SourceSecurityGroupId: !Ref InstanceSecurityGroup
DBSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: A4L Aurora subnet group
SubnetIds:
- !Ref SubnetDBA
- !Ref SubnetDBB
- !Ref SubnetDBC
DBCluster:
Type: "AWS::RDS::DBCluster"
DeletionPolicy: Delete
Properties:
DBSubnetGroupName: !Ref DBSubnetGroup
DatabaseName: !If [NoSnapshot, !Ref DBName, !Ref 'AWS::NoValue' ]
Engine: 'aurora-mysql'
EngineMode: 'provisioned'
EngineVersion: !Ref DBVersion
MasterUserPassword: !If [NoSnapshot, !Ref DBPassword, !Ref 'AWS::NoValue' ]
MasterUsername: !If [NoSnapshot, !Ref DBUser, !Ref 'AWS::NoValue' ]
SnapshotIdentifier: !If [ NoSnapshot, !Ref 'AWS::NoValue', !Ref DatabaseRestoreSnapshot]
Tags:
-
Key: Name
Value: "A4L-Aurora-Cluster"
VpcSecurityGroupIds:
- !Ref RDSSecurityGroup
AuroraInstance1:
Type: AWS::RDS::DBInstance
DeletionPolicy: Delete
Properties:
AllowMajorVersionUpgrade: false
AutoMinorVersionUpgrade: true
DBClusterIdentifier: !Ref DBCluster
DBInstanceClass: !Ref DBInstanceType
DBSubnetGroupName: !Ref DBSubnetGroup
Engine: 'aurora-mysql'
Tags:
- Key: Name
Value: !Join [ '', [ 'WordPress / ', !Ref 'AWS::StackName' ] ]
AuroraInstance2:
Type: AWS::RDS::DBInstance
DeletionPolicy: Delete
Properties:
AllowMajorVersionUpgrade: false
AutoMinorVersionUpgrade: true
DBClusterIdentifier: !Ref DBCluster
DBInstanceClass: !Ref DBInstanceType
DBSubnetGroupName: !Ref DBSubnetGroup
Engine: 'aurora-mysql'
Tags:
- Key: Name
Value: !Join [ '', [ 'WordPress / ', !Ref 'AWS::StackName' ] ]
InstanceSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
VpcId: !Ref VPC
GroupDescription: Enable SSH access via port 22 IPv4 & v6
SecurityGroupIngress:
- Description: 'Allow SSH IPv4 IN'
IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: '0.0.0.0/0'
- Description: 'Allow HTTP IPv4 IN'
IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: '0.0.0.0/0'
- Description: 'Allow SSH IPv6 IN'
IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIpv6: ::/0
InstanceSecurityGroupSelfReferenceRule:
Type: "AWS::EC2::SecurityGroupIngress"
Properties:
GroupId: !Ref InstanceSecurityGroup
IpProtocol: 'tcp'
FromPort: '0'
ToPort: '65535'
SourceSecurityGroupId: !Ref InstanceSecurityGroup
WordpressRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- 'sts:AssumeRole'
Path: /
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
- "arn:aws:iam::aws:policy/AmazonSSMFullAccess"
- "arn:aws:iam::aws:policy/AmazonElasticFileSystemClientFullAccess"
WordpressInstanceProfile:
Type: 'AWS::IAM::InstanceProfile'
Properties:
Path: /
Roles:
- !Ref WordpressRole
CWAgentConfig:
Type: AWS::SSM::Parameter
Properties:
Type: 'String'
Value: |
{
"agent": {
"metrics_collection_interval": 60,
"run_as_user": "root"
},
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/secure",
"log_group_name": "/var/log/secure",
"log_stream_name": "{instance_id}"
},
{
"file_path": "/var/log/httpd/access_log",
"log_group_name": "/var/log/httpd/access_log",
"log_stream_name": "{instance_id}"
},
{
"file_path": "/var/log/httpd/error_log",
"log_group_name": "/var/log/httpd/error_log",
"log_stream_name": "{instance_id}"
}
]
}
}
},
"metrics": {
"append_dimensions": {
"AutoScalingGroupName": "${aws:AutoScalingGroupName}",
"ImageId": "${aws:ImageId}",
"InstanceId": "${aws:InstanceId}",
"InstanceType": "${aws:InstanceType}"
},
"metrics_collected": {
"collectd": {
"metrics_aggregation_interval": 60
},
"cpu": {
"measurement": [
"cpu_usage_idle",
"cpu_usage_iowait",
"cpu_usage_user",
"cpu_usage_system"
],
"metrics_collection_interval": 60,
"resources": [
"*"
],
"totalcpu": false
},
"disk": {
"measurement": [
"used_percent",
"inodes_free"
],
"metrics_collection_interval": 60,
"resources": [
"*"
]
},
"diskio": {
"measurement": [
"io_time",
"write_bytes",
"read_bytes",
"writes",
"reads"
],
"metrics_collection_interval": 60,
"resources": [
"*"
]
},
"mem": {
"measurement": [
"mem_used_percent"
],
"metrics_collection_interval": 60
},
"netstat": {
"measurement": [
"tcp_established",
"tcp_time_wait"
],
"metrics_collection_interval": 60
},
"statsd": {
"metrics_aggregation_interval": 60,
"metrics_collection_interval": 10,
"service_address": ":8125"
},
"swap": {
"measurement": [
"swap_used_percent"
],
"metrics_collection_interval": 60
}
}
}
}
ElasticFileSystem:
Type: AWS::EFS::FileSystem
Properties:
FileSystemTags:
- Key: Name
Value: !Join [ '', [ 'A4L-EFS-WordPress / ', !Ref 'AWS::StackName' ] ]
ElasticFileSystemMountTarget0:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref "ElasticFileSystem"
SecurityGroups:
- !Ref "InstanceSecurityGroup"
SubnetId: !Ref "SubnetAPPA"
ElasticFileSystemMountTarget1:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref "ElasticFileSystem"
SecurityGroups:
- !Ref "InstanceSecurityGroup"
SubnetId: !Ref "SubnetAPPB"
ElasticFileSystemMountTarget2:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref "ElasticFileSystem"
SecurityGroups:
- !Ref "InstanceSecurityGroup"
SubnetId: !Ref "SubnetAPPC"
Outputs:
instanceprofile:
Description: "Default Instance Profile"
Value: !Ref WordpressInstanceProfile
Export:
Name: "A4L-WordpressInstanceProfile"
subnetweba:
Description: "Web A (Public) Subnet"
Value: !Ref SubnetWEBA
Export:
Name: "A4L-SubnetWEBA"
InstanceSecurityGroup:
Description: "A4L Default Instance SG"
Value: !Ref InstanceSecurityGroup
Export:
Name: "A4L-InstanceSecurityGroup"
dbendpoint:
Description: "RDS Endpoint Address"
Value: !GetAtt DBCluster.Endpoint.Address
Export:
Name: "A4L-DBENDPOINT"
ElasticFileSystem:
Value: !Ref ElasticFileSystem
Export:
Name: A4L-EFS
cwagentconfig:
Description: "CWAgent Config Parameter"
Value: !Ref "CWAgentConfig"
Export:
Name: "A4L-CWAGENT-CONFIG"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment