AWSTemplateFormatVersion: 2010-09-09 Description: Amazon Linux 2023 ( https://github.com/aws-samples/ec2-lamp-server ) (uksb-014citd1s9) (tag:AL2023) Transform: "AWS::LanguageExtensions" 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. AWS::CloudFormation::Interface: ParameterGroups: - Label: default: EC2 Instance Parameters: - ec2Name - ec2KeyPair - processorArchitecture - instanceType - ec2TerminationProtection - Label: default: EC2 Network Parameters: - vpcID - subnetID - displayPublicIP - assignStaticIP - Label: default: EC2 Remote Administration Parameters: - ingressIPv4 - ingressIPv6 - allowSSHport - installDCV - installWebmin - Label: default: LAMP Parameters: - webOption - phpVersion - databaseOption - installApp - Label: default: Others Parameters: - installDocker - enableR53acmeSupport - s3BucketName - Label: default: EBS Parameters: - volumeSize - volumeType - Label: default: Application Load Balancer (ALB) Parameters: - enableALB - albSubnets - albScheme - albIpAddressType - Label: default: ALB HTTPS Listener Parameters: - albCertificateArn - albSecurityPolicy - albRedirectHTTPtoHTTPS - albHstsHeaderValue - Label: default: Amazon CloudFront Parameters: - enableCloudFront - originType - Label: default: AWS Backup Parameters: - enableBackup - scheduleExpression - scheduleExpressionTimezone - deleteAfterDays ParameterLabels: processorArchitecture: default: "Processor architecture" instanceType: default: "Instance type (x86_64 or arm64)" ec2Name: default: "Instance name" ec2KeyPair: default: "Keypair name" ec2TerminationProtection: default: "Enable EC2 termination protection to prevent accidental deletion" volumeSize: default: "Volume size (GiB)" volumeType: default: "Volume type" vpcID: default: "VPC with outbound internet connectivity" subnetID: default: "Subnet in selected VPC with outbound internet connectivity" displayPublicIP: default: "EC2 in public subnet with public IP assigned?" assignStaticIP: default: "Elastic IP: assign static public internet IPv4 address" enableALB: default: "Deploy Application Load Balancer (ALB)" albScheme: default: "Load balancer scheme (internal or internet facing)" albIpAddressType: default: "IPv4 only, IPv6 only or dual stack (IPv4-and-IPv6)" albSubnets: default: "At least 2 subnets in EC2 VPC. **Select a subnet even if not deploying ALB**" albCertificateArn: default: "Certificate ARN" albSecurityPolicy: default: "HTTPS listener security policy" albRedirectHTTPtoHTTPS: default: "Redirect HTTP requests to HTTPS" albHstsHeaderValue: default: "HSTS (HTTP Strict Transport Security) header value. Leave blank not to send" ingressIPv4: default: "Allowed source prefix (IPv4)" ingressIPv6: default: "Allowed source prefix (IPv6)" allowSSHport: default: "Allow SSH from network" installDCV: default: "Install graphical desktop environment and Amazon DCV server" installWebmin: default: "Install Webmin web-based system administration tool" webOption: default: "Web server to install" databaseOption: default: "Database server to install" phpVersion: default: "PHP version to install" installApp: default: "Application stack to install" installDocker: default: "Install Docker" enableR53acmeSupport: default: "Enable Route 53 ACME protocol DNS-01 challenge support" s3BucketName: default: "S3 bucket name to grant access to" enableBackup: default: "Backup EC2 instance" scheduleExpression: default: "CRON expression specifying when AWS Backup initiates a backup job" scheduleExpressionTimezone: default: "Timezone to set backup schedule" deleteAfterDays: default: "Number of days after creation that a recovery point (backup) is deleted" enableCloudFront: default: "Create Amazon CloudFront distribution" originType: default: "CloudFront origin type" Parameters: processorArchitecture: Type: String Description: https://aws.amazon.com/ec2/graviton/ AllowedValues: - Intel/AMD (x86_64) - Graviton (arm64) Default: Graviton (arm64) ec2Name: Type: String #Description: EC2 instance name Default: Amazon Linux 2023 ec2KeyPair: Type: AWS::EC2::KeyPair::KeyName Description: https://console.aws.amazon.com/ec2/#KeyPairs ConstraintDescription: Specify a key pair AllowedPattern: ".+" instanceType: Type: String Description: https://console.aws.amazon.com/ec2/#InstanceTypes AllowedPattern: "^[a-z\\-\\d\\.]+$" ConstraintDescription: Specify valid EC2 instance type Default: m6g.large ec2TerminationProtection: Type: String Description: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_ChangingDisableAPITermination.html Default: "Yes" AllowedValues: - "Yes" - "No" vpcID: Type: AWS::EC2::VPC::Id Description: "https://console.aws.amazon.com/vpcconsole/home#vpcs:" AllowedPattern: .+ ConstraintDescription: Select a VPC subnetID: Type: AWS::EC2::Subnet::Id Description: "https://console.aws.amazon.com/vpcconsole/home#subnets:" AllowedPattern: .+ ConstraintDescription: Select a Subnet assignStaticIP: Type: String Description: "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html" AllowedValues: - "Yes" - "No" Default: "Yes" displayPublicIP: Type: String Description: "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-instance-addressing.html#concepts-public-addresses" AllowedValues: - "Yes" - "No" Default: "Yes" enableALB: Type: String Description: https://docs.aws.amazon.com/elasticloadbalancing/latest/application/index.html AllowedValues: - "Yes" - "No" Default: "No" albScheme: Type: String Description: https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-application-load-balancer.html#configure-load-balancer AllowedValues: - internet-facing - internal Default: internet-facing albIpAddressType: Type: String AllowedValues: - IPv4 - IPv4-and-IPv6 - IPv6 Default: IPv4 albSubnets: Type: List Description: "https://console.aws.amazon.com/vpcconsole/home#subnets:" ConstraintDescription: Select a subnet AllowedPattern: ".+" albCertificateArn: Type: String Description: "https://console.aws.amazon.com/acm/home?#/certificates/list ( aws acm list-certificates )" Default: "" albSecurityPolicy: Type: String Description: "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/describe-ssl-policies.html" AllowedValues: - ELBSecurityPolicy-TLS13-1-3-FIPS-2023-04 - ELBSecurityPolicy-TLS13-1-3-2021-06 - ELBSecurityPolicy-TLS13-1-2-Res-FIPS-2023-04 - ELBSecurityPolicy-TLS13-1-2-Res-2021-06 - ELBSecurityPolicy-TLS13-1-2-FIPS-2023-04 - ELBSecurityPolicy-TLS13-1-2-Ext2-FIPS-2023-04 - ELBSecurityPolicy-TLS13-1-2-Ext2-2021-06 - ELBSecurityPolicy-TLS13-1-2-Ext1-FIPS-2023-04 - ELBSecurityPolicy-TLS13-1-2-Ext1-2021-06 - ELBSecurityPolicy-TLS13-1-2-Ext0-FIPS-2023-04 - ELBSecurityPolicy-TLS13-1-2-2021-06 - ELBSecurityPolicy-TLS13-1-1-FIPS-2023-04 - ELBSecurityPolicy-TLS13-1-1-2021-06 - ELBSecurityPolicy-TLS13-1-0-FIPS-2023-04 - ELBSecurityPolicy-TLS13-1-0-2021-06 - ELBSecurityPolicy-TLS-1-2-Ext-2018-06 - ELBSecurityPolicy-TLS-1-2-2017-01 - ELBSecurityPolicy-TLS-1-1-2017-01 - ELBSecurityPolicy-TLS-1-0-2015-04 - ELBSecurityPolicy-FS-2018-06 - ELBSecurityPolicy-FS-1-2-Res-2020-10 - ELBSecurityPolicy-FS-1-2-Res-2019-08 - ELBSecurityPolicy-FS-1-2-2019-08 - ELBSecurityPolicy-FS-1-1-2019-08 - ELBSecurityPolicy-2016-08 Default: ELBSecurityPolicy-TLS13-1-2-2021-06 albRedirectHTTPtoHTTPS: Type: String AllowedValues: - "Yes" - "No" Default: "Yes" albHstsHeaderValue: Type: String Description: "https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Strict_Transport_Security_Cheat_Sheet.html" Default: "max-age=31536000; includeSubDomains" ingressIPv4: Type: String Description: e.g. 1.2.3.4/32, get your source IP from https://checkip.amazonaws.com AllowedPattern: "^\\d+\\.\\d+\\.\\d+\\.\\d+\\/\\d+$" ConstraintDescription: Specify valid IPv4 prefix Default: 0.0.0.0/0 ingressIPv6: Type: String Description: e.g. 1:2:3:4::/64, get your internet IPv6 address (if any) with tools such as https://ifconfig.co AllowedPattern: .+ ConstraintDescription: Specify valid IPv6 prefix Default: ::/0 allowSSHport: Type: String Description: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-linux-inst-ssh.html AllowedValues: - "Yes" - "No" Default: "Yes" installDCV: Type: String Description: https://aws.amazon.com/hpc/dcv/ AllowedValues: - "Yes" - "No" Default: "Yes" installWebmin: Type: String Description: https://webmin.com/ AllowedValues: - "Yes" - "No" Default: "No" webOption: Type: String #Description: Web server to install AllowedValues: - "Apache" - "Nginx" Default: "Apache" databaseOption: Type: String #Description: Database to install AllowedValues: - "MariaDB 11.4" - "MariaDB 10.11" - "MySQL8.4" - "MySQL8.0" - "PostgreSQL" - "none" Default: "MariaDB 11.4" phpVersion: Type: String #Description: PHP version to install AllowedValues: - "php8.1" - "php8.2" - "php8.3" - "php8.4" - "php8.5" - "none" Default: "php8.4" s3BucketName: Type: String Description: "https://console.aws.amazon.com/s3/buckets" Default: "" enableR53acmeSupport: Type: String Description: https://letsencrypt.org/docs/challenge-types https://certbot-dns-route53.readthedocs.io/ AllowedValues: - "Yes" - "No" Default: "Yes" volumeSize: Type: Number #Description: Volume size in GiBs MinValue: 10 MaxValue: 16384 Default: 50 volumeType: Type: String Description: https://aws.amazon.com/ebs/general-purpose/ AllowedValues: - gp3 - gp2 Default: gp3 enableBackup: Type: String Description: https://docs.aws.amazon.com/aws-backup/ AllowedValues: - "Yes" - "No" Default: "No" scheduleExpression: Type: String Description: https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-scheduled-rule-pattern.html AllowedPattern: .+ Default: "cron(0 1 ? * * *)" scheduleExpressionTimezone: # https://nodatime.org/TimeZones?version=2024a&format=json Type: String Description: https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html#time-zones AllowedValues: - Africa/Abidjan - Africa/Algiers - Africa/Bissau - Africa/Cairo - Africa/Casablanca - Africa/Ceuta - Africa/El_Aaiun - Africa/Johannesburg - Africa/Juba - Africa/Khartoum - Africa/Lagos - Africa/Maputo - Africa/Monrovia - Africa/Nairobi - Africa/Ndjamena - Africa/Sao_Tome - Africa/Tripoli - Africa/Tunis - Africa/Windhoek - America/Adak - America/Anchorage - America/Araguaina - America/Argentina/Buenos_Aires - America/Argentina/Catamarca - America/Argentina/Cordoba - America/Argentina/Jujuy - America/Argentina/La_Rioja - America/Argentina/Mendoza - America/Argentina/Rio_Gallegos - America/Argentina/Salta - America/Argentina/San_Juan - America/Argentina/San_Luis - America/Argentina/Tucuman - America/Argentina/Ushuaia - America/Asuncion - America/Bahia - America/Bahia_Banderas - America/Barbados - America/Belem - America/Belize - America/Boa_Vista - America/Bogota - America/Boise - America/Cambridge_Bay - America/Campo_Grande - America/Cancun - America/Caracas - America/Cayenne - America/Chicago - America/Chihuahua - America/Ciudad_Juarez - America/Costa_Rica - America/Cuiaba - America/Danmarkshavn - America/Dawson - America/Dawson_Creek - America/Denver - America/Detroit - America/Edmonton - America/Eirunepe - America/El_Salvador - America/Fort_Nelson - America/Fortaleza - America/Glace_Bay - America/Goose_Bay - America/Grand_Turk - America/Guatemala - America/Guayaquil - America/Guyana - America/Halifax - America/Havana - America/Hermosillo - America/Indiana/Indianapolis - America/Indiana/Knox - America/Indiana/Marengo - America/Indiana/Petersburg - America/Indiana/Tell_City - America/Indiana/Vevay - America/Indiana/Vincennes - America/Indiana/Winamac - America/Inuvik - America/Iqaluit - America/Jamaica - America/Juneau - America/Kentucky/Louisville - America/Kentucky/Monticello - America/La_Paz - America/Lima - America/Los_Angeles - America/Maceio - America/Managua - America/Manaus - America/Martinique - America/Matamoros - America/Mazatlan - America/Menominee - America/Merida - America/Metlakatla - America/Mexico_City - America/Miquelon - America/Moncton - America/Monterrey - America/Montevideo - America/New_York - America/Nome - America/Noronha - America/North_Dakota/Beulah - America/North_Dakota/Center - America/North_Dakota/New_Salem - America/Nuuk - America/Ojinaga - America/Panama - America/Paramaribo - America/Phoenix - America/Port-au-Prince - America/Porto_Velho - America/Puerto_Rico - America/Punta_Arenas - America/Rankin_Inlet - America/Recife - America/Regina - America/Resolute - America/Rio_Branco - America/Santarem - America/Santiago - America/Santo_Domingo - America/Sao_Paulo - America/Scoresbysund - America/Sitka - America/St_Johns - America/Swift_Current - America/Tegucigalpa - America/Thule - America/Tijuana - America/Toronto - America/Vancouver - America/Whitehorse - America/Winnipeg - America/Yakutat - Antarctica/Casey - Antarctica/Davis - Antarctica/Macquarie - Antarctica/Mawson - Antarctica/Palmer - Antarctica/Rothera - Antarctica/Troll - Antarctica/Vostok - Asia/Almaty - Asia/Amman - Asia/Anadyr - Asia/Aqtau - Asia/Aqtobe - Asia/Ashgabat - Asia/Atyrau - Asia/Baghdad - Asia/Baku - Asia/Bangkok - Asia/Barnaul - Asia/Beirut - Asia/Bishkek - Asia/Chita - Asia/Choibalsan - Asia/Colombo - Asia/Damascus - Asia/Dhaka - Asia/Dili - Asia/Dubai - Asia/Dushanbe - Asia/Famagusta - Asia/Gaza - Asia/Hebron - Asia/Ho_Chi_Minh - Asia/Hong_Kong - Asia/Hovd - Asia/Irkutsk - Asia/Jakarta - Asia/Jayapura - Asia/Jerusalem - Asia/Kabul - Asia/Kamchatka - Asia/Karachi - Asia/Kathmandu - Asia/Khandyga - Asia/Kolkata - Asia/Krasnoyarsk - Asia/Kuching - Asia/Macau - Asia/Magadan - Asia/Makassar - Asia/Manila - Asia/Nicosia - Asia/Novokuznetsk - Asia/Novosibirsk - Asia/Omsk - Asia/Oral - Asia/Pontianak - Asia/Pyongyang - Asia/Qatar - Asia/Qostanay - Asia/Qyzylorda - Asia/Riyadh - Asia/Sakhalin - Asia/Samarkand - Asia/Seoul - Asia/Shanghai - Asia/Singapore - Asia/Srednekolymsk - Asia/Taipei - Asia/Tashkent - Asia/Tbilisi - Asia/Tehran - Asia/Thimphu - Asia/Tokyo - Asia/Tomsk - Asia/Ulaanbaatar - Asia/Urumqi - Asia/Ust-Nera - Asia/Vladivostok - Asia/Yakutsk - Asia/Yangon - Asia/Yekaterinburg - Asia/Yerevan - Atlantic/Azores - Atlantic/Bermuda - Atlantic/Canary - Atlantic/Cape_Verde - Atlantic/Faroe - Atlantic/Madeira - Atlantic/South_Georgia - Atlantic/Stanley - Australia/Adelaide - Australia/Brisbane - Australia/Broken_Hill - Australia/Darwin - Australia/Eucla - Australia/Hobart - Australia/Lindeman - Australia/Lord_Howe - Australia/Melbourne - Australia/Perth - Australia/Sydney - CET - CST6CDT - EET - EST - EST5EDT - Etc/GMT - Etc/GMT+1 - Etc/GMT+10 - Etc/GMT+11 - Etc/GMT+12 - Etc/GMT+2 - Etc/GMT+3 - Etc/GMT+4 - Etc/GMT+5 - Etc/GMT+6 - Etc/GMT+7 - Etc/GMT+8 - Etc/GMT+9 - Etc/GMT-1 - Etc/GMT-10 - Etc/GMT-11 - Etc/GMT-12 - Etc/GMT-13 - Etc/GMT-14 - Etc/GMT-2 - Etc/GMT-3 - Etc/GMT-4 - Etc/GMT-5 - Etc/GMT-6 - Etc/GMT-7 - Etc/GMT-8 - Etc/GMT-9 - Etc/UTC - Europe/Andorra - Europe/Astrakhan - Europe/Athens - Europe/Belgrade - Europe/Berlin - Europe/Brussels - Europe/Bucharest - Europe/Budapest - Europe/Chisinau - Europe/Dublin - Europe/Gibraltar - Europe/Helsinki - Europe/Istanbul - Europe/Kaliningrad - Europe/Kirov - Europe/Kyiv - Europe/Lisbon - Europe/London - Europe/Madrid - Europe/Malta - Europe/Minsk - Europe/Moscow - Europe/Paris - Europe/Prague - Europe/Riga - Europe/Rome - Europe/Samara - Europe/Saratov - Europe/Simferopol - Europe/Sofia - Europe/Tallinn - Europe/Tirane - Europe/Ulyanovsk - Europe/Vienna - Europe/Vilnius - Europe/Volgograd - Europe/Warsaw - Europe/Zurich - HST - Indian/Chagos - Indian/Maldives - Indian/Mauritius - MET - MST - MST7MDT - PST8PDT - Pacific/Apia - Pacific/Auckland - Pacific/Bougainville - Pacific/Chatham - Pacific/Easter - Pacific/Efate - Pacific/Fakaofo - Pacific/Fiji - Pacific/Galapagos - Pacific/Gambier - Pacific/Guadalcanal - Pacific/Guam - Pacific/Honolulu - Pacific/Kanton - Pacific/Kiritimati - Pacific/Kosrae - Pacific/Kwajalein - Pacific/Marquesas - Pacific/Nauru - Pacific/Niue - Pacific/Norfolk - Pacific/Noumea - Pacific/Pago_Pago - Pacific/Palau - Pacific/Pitcairn - Pacific/Port_Moresby - Pacific/Rarotonga - Pacific/Tahiti - Pacific/Tarawa - Pacific/Tongatapu - WET Default: Etc/UTC deleteAfterDays: Type: Number Default: 35 installDocker: Type: String Description: https://docs.docker.com/engine/ AllowedValues: - "Yes" - "No" Default: "Yes" installApp: Type: String Description: https://make.wordpress.org/hosting/handbook/compatibility/ https://moodledev.io/general/releases AllowedValues: - "WordPress" - "Moodle 4.5 (LTS)" - "Moodle 5.0" - "Moodle 5.1" - "None" Default: "None" enableCloudFront: Type: String Description: Requires IPv4 origin. https://aws.amazon.com/cloudfront/ AllowedValues: - "Yes" - "No" Default: "No" originType: Type: String Description: Enable Elastic IP if using EC2 custom origin AllowedValues: - "Custom Origin" - "VPC Origin" Default: "Custom Origin" Conditions: useARM64: !Equals [!Ref processorArchitecture, "Graviton (arm64)"] displayPublicIP: !Equals [!Ref displayPublicIP, "Yes"] useElasticIP: !And [!Condition displayPublicIP, !Equals [!Ref assignStaticIP, "Yes"]] enableProtection: !Equals [!Ref ec2TerminationProtection, "Yes"] hasEIC: !Not [ !Equals [ !FindInMap [ EICprefixMap, !Ref AWS::Region, IpPrefix, DefaultValue: 127.0.0.1/32, ], 127.0.0.1/32, ], ] createSgEIC: !And [!Condition hasEIC, !Condition displayPublicIP] hasCFprefix: !Not [ !Equals [ !FindInMap [ CFprefixMap, !Ref AWS::Region, PrefixList, DefaultValue: pl-none, ], pl-none, ], ] createSgSSH: !Equals [!Ref allowSSHport, "Yes"] hasS3Bucket: !Not [!Equals [!Ref s3BucketName, ""]] hasR53Zone: !Equals [!Ref enableR53acmeSupport, "Yes"] installDCV: !Equals [!Ref installDCV, "Yes"] noDCV: !Not [!Condition installDCV] installWebmin: !Equals [!Ref installWebmin, "Yes"] createBackup: !Equals [!Ref enableBackup, "Yes"] createCloudFront: !Equals [!Ref enableCloudFront, "Yes"] cfVPCOrigin: !And [!Condition createCloudFront, !Equals [!Ref originType, "VPC Origin"]] createALB: !Equals [!Ref enableALB, "Yes"] albAllowIPv4: !Or [ !Equals [!Ref albIpAddressType, "IPv4"], !Equals [!Ref albIpAddressType, "IPv4-and-IPv6"], ] albAllowIPv6: !Or [ !Equals [!Ref albIpAddressType, "IPv6"], !Equals [!Ref albIpAddressType, "IPv4-and-IPv6"], ] createHttpsListener: !And [!Condition createALB, !Not [!Equals [!Ref albCertificateArn, ""]]] createHttpRedirectListener: !And [ !Condition createHttpsListener, !Equals [!Ref albRedirectHTTPtoHTTPS, "Yes"], ] createHttpListener: !And [!Condition createALB, !Not [!Condition createHttpRedirectListener]] sendHSTS: !Not [!Equals [!Ref albHstsHeaderValue, ""]] Mappings: # EC2 instance connect: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-connect-prerequisites.html#ec2-instance-connect-setup-security-group # curl -s https://ip-ranges.amazonaws.com/ip-ranges.json | jq -r '.prefixes[] | select (.service=="EC2_INSTANCE_CONNECT")' # curl -s https://ip-ranges.amazonaws.com/ip-ranges.json | jq -r '.ipv6_prefixes[] | select (.service=="EC2_INSTANCE_CONNECT")' EICprefixMap: af-south-1: IpPrefix: 13.244.121.196/30 Ipv6Prefix: 2406:da11:700:3b00::/56 ap-east-1: IpPrefix: 43.198.192.104/29 Ipv6Prefix: 2406:da1e:da1:3c00::/56 ap-northeast-1: IpPrefix: 3.112.23.0/29 Ipv6Prefix: 2406:da14:1c18:2100::/56 ap-northeast-2: IpPrefix: 13.209.1.56/29 Ipv6Prefix: 2406:da12:1e1:d900::/56 ap-northeast-3: IpPrefix: 15.168.105.160/29 Ipv6Prefix: 2406:da16:856:a500::/56 ap-south-1: IpPrefix: 13.233.177.0/29 Ipv6Prefix: 2406:da1a:74a:4b00::/56 ap-south-2: IpPrefix: 18.60.252.248/29 Ipv6Prefix: 2406:da1b:d1d:8800::/56 ap-southeast-1: IpPrefix: 3.0.5.32/29 Ipv6Prefix: 2406:da18:752:6600::/56 ap-southeast-2: IpPrefix: 13.239.158.0/29 Ipv6Prefix: 2406:da1c:90e:4a00::/56 ap-southeast-3: IpPrefix: 43.218.193.64/29 Ipv6Prefix: 2406:da19:14b:8c00::/56 ap-southeast-4: IpPrefix: 16.50.248.80/29 Ipv6Prefix: 2406:da1f:b4f:4600::/56 ap-southeast-5: IpPrefix: 43.216.87.48/29 Ipv6Prefix: 2406:da10:84f9:9e00::/56 ap-southeast-7: IpPrefix: 43.209.155.96/29 Ipv6Prefix: 2406:da14:85c5:5b00::/56 ca-central-1: IpPrefix: 35.183.92.176/29 Ipv6Prefix: 2600:1f11:ae3:700::/56 ca-west-1: IpPrefix: 40.176.213.168/29 Ipv6Prefix: 2600:1f1a:4ff6:d500::/56 cn-north-1: IpPrefix: 43.196.20.40/29 Ipv6Prefix: 2400:7fc0:86fd:e00::/56 cn-northwest-1: IpPrefix: 43.192.155.8/29 Ipv6Prefix: 2404:c2c0:87aa:4800::/56 eu-central-1: IpPrefix: 3.120.181.40/29 Ipv6Prefix: 2a05:d014:17a8:8b00::/56 eu-central-2: IpPrefix: 16.63.77.8/29 Ipv6Prefix: 2a05:d019:1d6:2100::/56 eu-north-1: IpPrefix: 13.48.4.200/30 Ipv6Prefix: 2a05:d016:494:f00::/56 eu-south-1: IpPrefix: 15.161.135.164/30 Ipv6Prefix: 2a05:d01a:c03:4a00::/56 eu-south-2: IpPrefix: 18.101.90.48/29 Ipv6Prefix: 2a05:d011:cbe:f700::/56 eu-west-1: IpPrefix: 18.202.216.48/29 Ipv6Prefix: 2a05:d018:403:4e00::/56 eu-west-2: IpPrefix: 3.8.37.24/29 Ipv6Prefix: 2a05:d01c:4ac:3100::/56 eu-west-3: IpPrefix: 35.180.112.80/29 Ipv6Prefix: 2a05:d012:c9e:d600::/56 il-central-1: IpPrefix: 51.16.183.224/29 Ipv6Prefix: 2a05:d025:451:7d00::/56 me-central-1: IpPrefix: 3.29.147.40/29 Ipv6Prefix: 2406:da17:1db:b00::/56 me-south-1: IpPrefix: 16.24.46.56/29 Ipv6Prefix: 2a05:d01e:27f:ac00::/56 mx-central-1: IpPrefix: 78.12.207.8/29 Ipv6Prefix: 22600:1f17:4ee0:b800::/56 sa-east-1: IpPrefix: 18.228.70.32/29 Ipv6Prefix: 2600:1f1e:d1d:e700::/56 us-east-1: IpPrefix: 18.206.107.24/29 Ipv6Prefix: 2600:1f18:6fe3:8c00::/56 us-east-2: IpPrefix: 3.16.146.0/29 Ipv6Prefix: 2600:1f16:138f:cf00::/56 us-gov-east-1: IpPrefix: 18.252.4.0/30 Ipv6Prefix: 2600:1f15:d63:bd00::/56 us-gov-west-1: IpPrefix: 15.200.28.80/30 Ipv6Prefix: 2600:1f12:fa9:5100::/56 us-west-1: IpPrefix: 13.52.6.112/29 Ipv6Prefix: 2600:1f1c:12d:e900::/56 us-west-2: IpPrefix: 18.237.140.160/29 Ipv6Prefix: 2600:1f13:a0d:a700::/56 CFprefixMap: # aws ec2 describe-managed-prefix-lists --query "PrefixLists[?PrefixListName=='com.amazonaws.global.cloudfront.origin-facing']" --region af-south-1: PrefixList: pl-c0aa4fa9 Ipv6PrefixList: pl-08e545c506fc11b3d ap-east-1: PrefixList: pl-14b2577d Ipv6PrefixList: pl-09eb2ebd84c23b987 ap-east-2: PrefixList: pl-0b51e244975ca1f58 Ipv6PrefixList: pl-0675c4405f2d19014 ap-northeast-1: PrefixList: pl-58a04531 Ipv6PrefixList: pl-0f28cd4a128e7b13a ap-northeast-2: PrefixList: pl-22a6434b Ipv6PrefixList: pl-07ac407da2b364d6c ap-northeast-3: PrefixList: pl-31a14458 Ipv6PrefixList: pl-04e68c40b871c8e6b ap-south-1: PrefixList: pl-9aa247f3 Ipv6PrefixList: pl-029b73ad1ccf6fe97 ap-south-2: PrefixList: pl-0a25c3463226fcc61 Ipv6PrefixList: pl-045b5138c20f83bab ap-southeast-1: PrefixList: pl-31a34658 Ipv6PrefixList: pl-02d26f62e3b1ed532 ap-southeast-2: PrefixList: pl-b8a742d1 Ipv6PrefixList: pl-033521892361c13a7 ap-southeast-3: PrefixList: pl-bca247d5 Ipv6PrefixList: pl-0b8932aa3ef329011 ap-southeast-4: PrefixList: pl-0fb7e7cfe038ae0e9 Ipv6PrefixList: pl-03292f9327ecaf81c ap-southeast-5: PrefixList: pl-09076f83e90b139d0 Ipv6PrefixList: pl-015db6a9a3f8b7f38 ap-southeast-6: PrefixList: pl-04ed52d45e258dfd3 Ipv6PrefixList: pl-04ec346b0299ab3e3 ap-southeast-7: PrefixList: pl-0857de2e2b1c7f2a2 Ipv6PrefixList: pl-0d394c8e84df4ca56 ca-central-1: PrefixList: pl-38a64351 Ipv6PrefixList: pl-0c0fd74227163049a ca-west-1: PrefixList: pl-0530d4c590b35122b Ipv6PrefixList: pl-0cd01c4f03b66d585 eu-central-1: PrefixList: pl-a3a144ca Ipv6PrefixList: pl-0624f1d638a3e93df eu-central-2: PrefixList: pl-00b37293991dbe6a8 Ipv6PrefixList: pl-05ff33f3c280b2eb8 eu-north-1: PrefixList: pl-fab65393 Ipv6PrefixList: pl-05de9757262c679ba eu-south-1: PrefixList: pl-1bbc5972 Ipv6PrefixList: pl-07b149cb3ccb7e2da eu-south-2: PrefixList: pl-052dcbe0f793f19da Ipv6PrefixList: pl-0454b1d06a3e15f2f eu-west-1: PrefixList: pl-4fa04526 Ipv6PrefixList: pl-010bae2278f1a872d eu-west-2: PrefixList: pl-93a247fa Ipv6PrefixList: pl-0d7c235a121ad5fd1 eu-west-3: PrefixList: pl-75b1541c Ipv6PrefixList: pl-06d246df64d68cb0a il-central-1: PrefixList: pl-0dd89524416301988 Ipv6PrefixList: pl-023722c7ce8718ca2 me-central-1: PrefixList: pl-05266a86378662c23 Ipv6PrefixList: pl-08b3f3c9dc8f45b09 me-south-1: PrefixList: pl-17b2577e Ipv6PrefixList: pl-04baf1fb5ff7e9290 mx-central-1: PrefixList: pl-0246509e78ddf0729 Ipv6PrefixList: pl-0df0f56c679a42f28 sa-east-1: PrefixList: pl-5da64334 Ipv6PrefixList: pl-0051b342e8bc54805 us-east-1: PrefixList: pl-3b927c52 Ipv6PrefixList: pl-02d12e369a4312e03 us-east-2: PrefixList: pl-b6a144df Ipv6PrefixList: pl-079a97b94f32e4ee7 us-west-1: PrefixList: pl-4ea04527 Ipv6PrefixList: pl-06dd7c6e345937257 us-west-2: PrefixList: pl-82a045eb Ipv6PrefixList: pl-07f8c64944f5dc195 albIpTypeMapping: IPv4: Value: ipv4 IPv4-and-IPv6: Value: dualstack IPv6: Value: dualstack-without-public-ipv4 Resources: instanceIamRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: [ec2.amazonaws.com] Action: [sts:AssumeRole] Path: / Policies: - !If - installDCV - PolicyName: dcvLicensing PolicyDocument: # https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-license.html Version: "2012-10-17" Statement: - Effect: Allow Action: - s3:GetObject Resource: !Sub "arn:*:s3:::dcv-license.${AWS::Region}/*" - !Ref AWS::NoValue - !If - hasS3Bucket - PolicyName: MountPointS3Access PolicyDocument: # MountPoint for S3: https://github.com/awslabs/mountpoint-s3/blob/main/doc/CONFIGURATION.md Version: "2012-10-17" Statement: - Effect: Allow Action: - s3:ListBucket Resource: !Sub "arn:${AWS::Partition}:s3:::${s3BucketName}" - Effect: Allow Action: - s3:GetObject - s3:PutObject - s3:AbortMultipartUpload - s3:DeleteObject Resource: !Sub "arn:${AWS::Partition}:s3:::${s3BucketName}/*" - !Ref AWS::NoValue - !If - hasS3Bucket - PolicyName: MountPointS3ExpressAccess PolicyDocument: # MountPoint for S3: https://github.com/awslabs/mountpoint-s3/blob/main/doc/CONFIGURATION.md Version: "2012-10-17" Statement: - Effect: Allow Action: - s3express:CreateSession Resource: !Sub "arn:${AWS::Partition}:s3express:${AWS::Region}:${AWS::AccountId}:bucket/${s3BucketName}--az_id--x-s3" - !Ref AWS::NoValue - !If - hasR53Zone - PolicyName: R53acmeAccess PolicyDocument: # Certbot dns_route53 : https://certbot-dns-route53.readthedocs.io/en/stable/ Version: "2012-10-17" Statement: # https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/specifying-rrset-conditions.html - Effect: Allow Action: - route53:ListHostedZones - route53:GetChange Resource: "*" - Effect: Allow Action: - route53:ChangeResourceRecordSets Resource: !Sub arn:${AWS::Partition}:route53:::hostedzone/* Condition: IpAddress: aws:SourceIp: 0.0.0.0/0 ForAllValues:StringEquals: route53:ChangeResourceRecordSetsRecordTypes: [TXT] ForAllValues:StringLike: route53:ChangeResourceRecordSetsNormalizedRecordNames: [_acme-challenge.*] - !Ref AWS::NoValue - !If - hasR53Zone - PolicyName: Route53UpdaterAccess PolicyDocument: Version: "2012-10-17" Statement: # https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/specifying-rrset-conditions.html - Effect: Allow Action: - route53:ListHostedZones - route53:GetChange Resource: "*" - Effect: Allow Action: - route53:ChangeResourceRecordSets Resource: !Sub arn:${AWS::Partition}:route53:::hostedzone/* Condition: IpAddress: aws:SourceIp: 0.0.0.0/0 ForAllValues:StringEquals: route53:ChangeResourceRecordSetsRecordTypes: [A] ForAllValues:StringLike: route53:ChangeResourceRecordSetsNormalizedRecordNames: ["*.example.com"] - !Ref AWS::NoValue ManagedPolicyArns: - !Sub "arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore" - !Sub "arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy" - !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonEC2RoleforAWSCodeDeployLimited" Tags: - Key: StackName Value: !Ref AWS::StackName - Key: StackId Value: !Ref AWS::StackId - Key: GitHub Value: https://github.com/aws-samples/ec2-lamp-server instanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: - !Ref instanceIamRole sgEC2Instance: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Allow inbound HTTP, HTTPS and any remote admin ports VpcId: !Ref vpcID SecurityGroupIngress: - !If - installWebmin - Description: Webmin (IPv4) IpProtocol: tcp FromPort: 10000 ToPort: 10000 CidrIp: !Ref ingressIPv4 - !Ref AWS::NoValue - !If - installWebmin - Description: Webmin (IPv6) IpProtocol: tcp FromPort: 10000 ToPort: 10000 CidrIpv6: !Ref ingressIPv6 - !Ref AWS::NoValue - !If - createSgSSH - Description: SSH (IPv4) IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: !Ref ingressIPv4 - !Ref AWS::NoValue - !If - createSgSSH - Description: SSH (IPv6) IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIpv6: !Ref ingressIPv6 - !Ref AWS::NoValue - !If - installDCV - Description: DCV (IPv4) IpProtocol: tcp FromPort: 8443 ToPort: 8443 CidrIp: !Ref ingressIPv4 - !Ref AWS::NoValue - !If - installDCV - Description: DCV (IPv6) IpProtocol: tcp FromPort: 8443 ToPort: 8443 CidrIpv6: !Ref ingressIPv6 - !Ref AWS::NoValue - !If - installDCV - Description: DCV QUIC (IPv4) IpProtocol: udp FromPort: 8443 ToPort: 8443 CidrIp: !Ref ingressIPv4 - !Ref AWS::NoValue - !If - installDCV - Description: DCV QUIC (IPv6) IpProtocol: udp FromPort: 8443 ToPort: 8443 CidrIpv6: !Ref ingressIPv6 - !Ref AWS::NoValue - Description: HTTP (IPv4) IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 - Description: HTTP (IPv6) IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIpv6: ::/0 - Description: HTTPS (IPv4) IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 0.0.0.0/0 - Description: HTTPS (IPv6) IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIpv6: ::/0 - !If - createSgEIC - Description: SSH (EC2 Instance Connect IPv4) IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: !FindInMap [EICprefixMap, !Ref AWS::Region, IpPrefix] - !Ref AWS::NoValue - !If - createSgEIC - Description: SSH (EC2 Instance Connect IPv6) IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIpv6: !FindInMap [EICprefixMap, !Ref AWS::Region, Ipv6Prefix] - !Ref AWS::NoValue - !If - createALB - Description: Allow ALB inbound IpProtocol: "-1" SourceSecurityGroupId: !Ref albSecurityGroup - !Ref AWS::NoValue SecurityGroupEgress: - Description: Allow all outbound traffic (IPv4) IpProtocol: "-1" CidrIp: 0.0.0.0/0 - Description: Allow all outbound traffic (IPv6) IpProtocol: "-1" CidrIpv6: ::/0 Tags: - Key: StackName Value: !Ref AWS::StackName - Key: StackId Value: !Ref AWS::StackId - Key: Name Value: !Sub - "${AWS::StackName}-securityGroup-${UID}" - UID: !Select [ 3, !Split ["-", !Select [2, !Split ["/", !Ref AWS::StackId]]], ] - Key: GitHub Value: https://github.com/aws-samples/ec2-lamp-server sgCloudFrontIPv4: Type: AWS::EC2::SecurityGroup Condition: hasCFprefix Properties: GroupDescription: Allow inbound HTTP from CloudFront (IPv4) VpcId: !Ref vpcID SecurityGroupIngress: - Description: HTTP (CloudFront origin IPv4) IpProtocol: tcp FromPort: 80 ToPort: 80 SourcePrefixListId: !FindInMap [CFprefixMap, !Ref AWS::Region, PrefixList] SecurityGroupEgress: - Description: Ping (CloudFront origin IPv4) IpProtocol: icmp FromPort: -1 ToPort: -1 DestinationPrefixListId: !FindInMap [CFprefixMap, !Ref AWS::Region, PrefixList] Tags: - Key: StackName Value: !Ref AWS::StackName - Key: StackId Value: !Ref AWS::StackId - Key: Name Value: !Sub - "${AWS::StackName}-CloudFrontHTTP-${UID}" - UID: !Select [ 3, !Split ["-", !Select [2, !Split ["/", !Ref AWS::StackId]]], ] - Key: GitHub Value: https://github.com/aws-samples/ec2-lamp-server sgCloudFrontIPv6: Type: AWS::EC2::SecurityGroup Condition: hasCFprefix Properties: GroupDescription: Allow inbound HTTP from CloudFront (IPv6) VpcId: !Ref vpcID SecurityGroupIngress: - Description: HTTP (CloudFront origin IPv6) IpProtocol: tcp FromPort: 80 ToPort: 80 SourcePrefixListId: !FindInMap [CFprefixMap, !Ref AWS::Region, Ipv6PrefixList] SecurityGroupEgress: - Description: Ping (CloudFront origin IPv6) IpProtocol: icmp FromPort: -1 ToPort: -1 DestinationPrefixListId: !FindInMap [CFprefixMap, !Ref AWS::Region, Ipv6PrefixList] Tags: - Key: StackName Value: !Ref AWS::StackName - Key: StackId Value: !Ref AWS::StackId - Key: Name Value: !Sub - "${AWS::StackName}-CloudFrontHTTPS-${UID}" - UID: !Select [ 3, !Split ["-", !Select [2, !Split ["/", !Ref AWS::StackId]]], ] - Key: GitHub Value: https://github.com/aws-samples/ec2-lamp-server ec2Instance: Type: AWS::EC2::Instance CreationPolicy: ResourceSignal: Timeout: PT90M Metadata: Comment: Install Update files AWS::CloudFormation::Init: configSets: setup: - 00_setup dcv_install: - 00_dcv_install php_install: - 01_php_install lamp_install: - 02_lamp_install 00_setup: # in the following order: packages, groups, users, sources, files, commands, and then services. files: "/home/ec2-user/update-dcv": content: | #!/bin/bash cd /tmp sudo rm -f /tmp/nice-dcv-amzn2023-$(arch).tgz wget https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-amzn2023-$(arch).tgz tar -xvzf nice-dcv-amzn2023-$(arch).tgz && cd nice-dcv-*-amzn2023-$(arch) sudo dnf install -y ./nice-dcv-server-*.rpm sudo dnf install -y ./nice-dcv-web-viewer-*.rpm sudo dnf install -y ./nice-xdcv-*.rpm sudo systemctl daemon-reload mode: "000755" owner: "ec2-user" group: "ec2-user" "/etc/systemd/system/dcv-virtual-session.service": content: | [Unit] Description=Create DCV virtual session After=default.target network.target [Service] ExecStart=/opt/dcv-virtual-session.sh [Install] WantedBy=default.target mode: "000644" owner: "root" group: "root" "/opt/dcv-virtual-session.sh": content: | #!/bin/bash dcvUsers=( "ec2-user" ) while true; do for dcvUser in "${dcvUsers[@]}" do if (! /usr/bin/dcv list-sessions | grep -q $dcvUser); then /usr/bin/dcv create-session $dcvUser --owner $dcvUser --storage-root %home% --type virtual fi done date /usr/bin/dcv list-sessions sleep 5 done mode: "000744" owner: "root" group: "root" "/etc/systemd/system/dcv-post-reboot.service": content: | [Unit] Description=Post install tasks After=default.target network.target [Service] ExecStart=/bin/sh -c "/opt/dcv-post-reboot.sh 2>&1 | tee -a /var/log/install-sw.log" [Install] WantedBy=default.target mode: "000644" owner: "root" group: "root" "/opt/dcv-post-reboot.sh": content: !Sub | #!/bin/bash #sysctl -w net.ipv6.conf.all.disable_ipv6=1 #sysctl -w net.ipv6.conf.default.disable_ipv6=1 dnf update -q -y # https://docs.aws.amazon.com/linux/al2023/ug/managing-repos-os-updates.html#automatic-restart-services dnf install -q -y smart-restart touch /etc/smart-restart-conf.d/0-pre-restart chmod +x /etc/smart-restart-conf.d/0-pre-restart touch /etc/smart-restart-conf.d/0-post-restart chmod +x /etc/smart-restart-conf.d/0-post-restart #sysctl -w net.ipv6.conf.all.disable_ipv6=0 #sysctl -w net.ipv6.conf.default.disable_ipv6=0 # DCV? export installDCV="${installDCV}" case $installDCV in Yes) systemctl enable --now dcv-virtual-session systemctl enable --now dcvserver ;; No) rm -f /etc/systemd/system/dcv-virtual-session.service rm -f /opt/dcv-virtual-session.sh rm -f /home/ec2-user/update-dcv ;; esac # GPU driver for G/P instance : https://repost.aws/articles/ARwfQMxiC-QMOgWykD9mco1w/ dnf install -q -y pciutils if ( lspci | grep -q NVIDIA ); then cd /tmp dnf install -q -y dkms systemctl enable dkms dnf install -q -y vulkan-devel libglvnd-devel elfutils-libelf-devel xorg-x11-server-Xorg K_VER=$(uname -r) K_MAJOR_VER=$(echo $K_VER | cut -d. -f1-2) case $KVER in 6.1.*) dnf install -q -y kernel dnf install -q -y kernel-headers kernel-devel kernel-modules-extra kernel-modules-extra-common --allowerasing ;; *) dnf install -q -y kernel$K_MAJOR_VER dnf install -q -y kernel$K_MAJOR_VER-headers kernel$K_MAJOR_VER8-devel kernel$K_MAJOR_VER-modules-extra kernel$K_MAJOR_VER-modules-extra-common --allowerasing ;; esac if (arch | grep -q x86); then ARCH=x86_64 else ARCH=sbsa fi dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/amzn2023/$ARCH/cuda-amzn2023.repo dnf clean expire-cache if ( ec2-metadata -t | grep -q 6f\. ); then # NVIDIA GRID: https://repost.aws/articles/ARHkpw_xUVR5qVgPQIU6F7AA/ aws s3 cp --recursive s3://ec2-linux-nvidia-drivers/latest/ . --no-sign-request chmod +x NVIDIA-Linux-x86_64*.run ./NVIDIA-Linux-x86_64*.run -s echo "options nvidia NVreg_EnableGpuFirmware=0" | tee --append /etc/modprobe.d/nvidia.conf else dnf module enable -y nvidia-driver:open-dkms dnf install -q -y nvidia-open dnf install -q -y nvidia-xconfig if ( ec2-metadata -t | grep -q " p[0-9]" ); then dnf install -q -y nvidia-fabricmanager libnvidia-nscq libnvsdm nvidia-imex if ( ec2-metadata -t | grep -q " p[6-9]" ); then dnf install -q -y nvlsm nvlink5 echo "ib_umad" | tee -a /etc/modules-load.d/modules.conf modprobe ib_umad fi systemctl enable --now nvidia-fabricmanager fi fi if [ "${installDocker}" = "Yes" ]; then if (! dnf search nvidia | grep -q nvidia-container-toolkit); then sudo dnf config-manager --add-repo https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo sudo dnf clean expire-cache fi dnf install -q -y nvidia-container-toolkit nvidia-ctk runtime configure --runtime=docker systemctl restart docker fi fi # IP address cert: https://letsencrypt.org/2026/03/11/shorter-certs-certbot if [ "${displayPublicIP}" = "Yes" ]; then # Some Regions create Elastic IP after EC2 instance is created /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ec2Instance --region ${AWS::Region} sleep 45 # web server reload script mv /opt/web-server-renew-cert.sh /etc/letsencrypt/renewal-hooks/deploy/web-server-renew-cert.sh case ${webOption} in Apache) WEB_ROOT="/var/www/html/" if (echo '${installApp}' | grep -q "Moodle 5.1"); then WEB_ROOT="/var/www/html/public/" fi ;; Nginx) WEB_ROOT="/usr/share/nginx/html/" if (echo '${installApp}' | grep -q "Moodle 5.1"); then WEB_ROOT="/usr/share/nginx/html/public/" fi ;; esac IP_ADDR=$(curl -s checkip.amazonaws.com) certbot certonly -n --agree-tos --preferred-profile shortlived --webroot --webroot-path $WEB_ROOT --ip-address $IP_ADDR if [ -d "/etc/letsencrypt/live/$IP_ADDR/" ]; then # Update web server cert case ${webOption} in Apache) rm -f /etc/pki/tls/certs/localhost.crt rm -f /etc/pki/tls/private/localhost.key ln -s -f /etc/letsencrypt/live/$IP_ADDR/fullchain.pem /etc/pki/tls/certs/localhost.crt ln -s -f /etc/letsencrypt/live/$IP_ADDR/privkey.pem /etc/pki/tls/private/localhost.key systemctl reload httpd ;; Nginx) rm -f /etc/pki/tls/nginx/cert.pem rm -f /etc/pki/tls/nginx/key.pem ln -s -f /etc/letsencrypt/live/$IP_ADDR/fullchain.pem /etc/pki/tls/nginx/cert.pem ln -s -f /etc/letsencrypt/live/$IP_ADDR/privkey.pem /etc/pki/tls/nginx/key.pem systemctl reload nginx ;; esac # DCV TLS cert: https://docs.aws.amazon.com/dcv/latest/adminguide/manage-cert.html if [ "${installDCV}" = "Yes" ]; then cp -f /etc/letsencrypt/live/$IP_ADDR/fullchain.pem /etc/dcv/dcv.pem cp -f /etc/letsencrypt/live/$IP_ADDR/privkey.pem /etc/dcv/dcv.key chown dcv /etc/dcv/dcv.pem /etc/dcv/dcv.key chmod 600 /etc/dcv/dcv.pem /etc/dcv/dcv.key # post cert renewal hook mv /opt/dcv-renew-cert.sh /etc/letsencrypt/renewal-hooks/deploy/opt/dcv-renew-cert.sh fi fi fi if [ -f "/opt/dcv-renew-cert.sh" ]; then rm /opt/dcv-renew-cert.sh fi if [ -f "/opt/web-server-renew-cert.sh" ]; then rm /opt/web-server-renew-cert.sh fi /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ec2Instance --region ${AWS::Region} # Supplementary Packages for Amazon Linux (SPAL): https://docs.aws.amazon.com/linux/al2023/ug/spal.html dnf install -q -y spal-release rm -f /etc/systemd/system/dcv-post-reboot.service rm -f ${!0} systemctl daemon-reload mode: "000755" owner: "root" group: "root" "/opt/dcv-renew-cert.sh": content: | #!/bin/bash CERT_FOLDER=$(curl -s checkip.amazonaws.com) if [ -d "/etc/letsencrypt/live/$CERT_FOLDER/" ]; then cp -f /etc/letsencrypt/live/$CERT_FOLDER/fullchain.pem /etc/dcv/dcv.pem cp -f /etc/letsencrypt/live/$CERT_FOLDER/privkey.pem /etc/dcv/dcv.key chown dcv /etc/dcv/dcv.pem /etc/dcv/dcv.key chmod 600 /etc/dcv/dcv.pem /etc/dcv/dcv.key fi mode: "000755" owner: "root" group: "root" "/opt/web-server-renew-cert.sh": content: | #!/bin/bash if (systemctl status httpd | grep -q running); then systemctl reload httpd fi if (systemctl status nginx | grep -q running); then systemctl reload nginx fi mode: "000755" owner: "root" group: "root" "/opt/aws/amazon-cloudwatch-agent/bin/config.json": content: | { "agent": { "metrics_collection_interval": 300, "run_as_user": "cwagent" }, "metrics": { "namespace": "CWAgent", "append_dimensions": { "InstanceId": "${aws:InstanceId}" }, "metrics_collected": { "mem": { "measurement": [ "used_percent" ] }, "disk": { "measurement": [ "used_percent" ], "resources": [ "/" ] } } } } mode: "000644" owner: "root" group: "root" "/etc/yum.repos.d/fedora.repo": # Fedora repo: disabled https://repost.aws/questions/QUIYtGINewTZ2k4tkp2QqeEw/php-repository-installation content: | [fedora] name=Fedora 36 - $basearch #baseurl=http://download.example/pub/fedora/linux/releases/36/Everything/$basearch/os/ metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-36&arch=$basearch enabled=0 countme=1 metadata_expire=7d repo_gpgcheck=0 type=rpm gpgcheck=0 gpgkey=https://getfedora.org/static/fedora.gpg skip_if_unavailable=False mode: "000644" owner: "root" group: "root" "/root/install-sw.sh": content: !Sub | #!/bin/bash mkdir -p /tmp/cfn cd /tmp/cfn # Change password PWD=$(cat /var/lib/cloud/data/instance-id) USER=ec2-user echo "$USER:$PWD" | chpasswd # Update OS dnf clean all dnf update -q -y sleep 10 pkill "^dnf$" # AWS CLI echo "export AWS_CLI_AUTO_PROMPT=on-partial" >> /home/ec2-user/.bashrc # dnf-automatic: https://dnf.readthedocs.io/en/latest/automatic.html dnf install -q -y dnf-automatic sed -i 's/apply_updates = no/apply_updates = yes/g' /etc/dnf/automatic.conf sed -i 's/emit_via = stdio/emit_via = motd/g' /etc/dnf/automatic.conf systemctl enable dnf-automatic-install.timer # https://docs.aws.amazon.com/linux/al2023/ug/deterministic-upgrades-usage.html#deterministic-upgrade-override-persist echo latest | sudo tee /etc/dnf/vars/releasever # CloudWatch agent: https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/install-CloudWatch-Agent-on-EC2-Instance.html dnf install -q -y amazon-cloudwatch-agent /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json -s systemctl enable --now amazon-cloudwatch-agent # Enable crontab dnf install -q -y cronie systemctl enable --now crond # Webmin: https://webmin.com/download/ export webmin="${installWebmin}" case $webmin in Yes) cd /tmp/cfn curl -s -L -O https://raw.githubusercontent.com/webmin/webmin/master/setup-repos.sh echo 'Y' | sh ./setup-repos.sh -f dnf install -q -y webmin ;; esac # USB and GPU driver DKMS dnf install -q -y dkms systemctl enable dkms # Kernel headers for GPU and USB remotization K_VER=$(uname -r) K_MAJOR_VER=$(echo $K_VER | cut -d. -f1-2) case $KVER in 6.1.*) dnf install -q -y kernel dnf install -q -y kernel-headers kernel-devel kernel-modules-extra kernel-modules-extra-common --allowerasing ;; *) dnf install -q -y kernel$K_MAJOR_VER dnf install -q -y kernel$K_MAJOR_VER-headers kernel$K_MAJOR_VER8-devel kernel$K_MAJOR_VER-modules-extra kernel$K_MAJOR_VER-modules-extra-common --allowerasing ;; esac # Docker: if [ "${installDocker}" = "Yes" ]; then dnf install -q -y docker systemctl enable docker usermod -aG docker ec2-user # docker-compose: https://github.com/amazonlinux/amazon-linux-2023/issues/186 mkdir -p /usr/local/lib/docker/cli-plugins curl -s -L https://github.com/docker/compose/releases/latest/download/docker-compose-linux-"$(arch)" -o /usr/local/lib/docker/cli-plugins/docker-compose chmod +x /usr/local/lib/docker/cli-plugins/docker-compose fi # Certbot: https://github.com/amazonlinux/amazon-linux-2023/issues/444 dnf install -q -y python3.14 python3.14-pip dnf install -q -y python3.14-devel augeas-devel python3.14 -m venv /opt/certbot /opt/certbot/bin/pip install --upgrade pip /opt/certbot/bin/pip install certbot /opt/certbot/bin/pip install certbot-dns-route53 /opt/certbot/bin/pip install certbot-apache /opt/certbot/bin/pip install certbot-nginx ln -s -f /opt/certbot/bin/certbot /usr/bin/certbot touch /etc/sysconfig/certbot chmod og-rwx /etc/sysconfig/certbot systemctl daemon-reload systemctl enable --now certbot-renew.timer # https://aws.amazon.com/about-aws/whats-new/2025/11/mountpoint-amazon-s3-amazon-linux-2023/ # https://docs.aws.amazon.com/linux/al2023/release-notes/relnotes-2023.9.20251110.html#amis-2023.9.20251110.Core-New-Packages dnf install -q -y mount-s3 # Amazon EFS Utils: https://docs.aws.amazon.com/efs/latest/ug/using-amazon-efs-utils.html dnf install -q -y amazon-efs-utils rm -f ${!0} mode: "000740" owner: "root" group: "root" "/usr/lib/systemd/system/certbot-renew.timer": content: | [Unit] Description=This is the timer to set the schedule for automated renewals [Timer] OnCalendar=*-*-* 00/12:00:00 RandomizedDelaySec=12hours Persistent=true [Install] WantedBy=timers.target mode: "000640" owner: "root" group: "root" "/usr/lib/systemd/system/certbot-renew.service": content: | [Unit] Description=This service automatically renews any certbot certificates found [Service] EnvironmentFile=/etc/sysconfig/certbot Type=oneshot ExecStart=/usr/bin/certbot renew --noninteractive --no-random-sleep-on-renew $PRE_HOOK $POST_HOOK $RENEW_HOOK $DEPLOY_HOOK $CERTBOT_ARGS mode: "000640" owner: "root" group: "root" "/home/ec2-user/update-certbot": content: | #!/bin/bash sudo /opt/certbot/bin/pip install --upgrade pip sudo /opt/certbot/bin/pip install --upgrade certbot sudo /opt/certbot/bin/pip install --upgrade certbot-dns-route53 sudo /opt/certbot/bin/pip install --upgrade certbot-apache sudo /opt/certbot/bin/pip install --upgrade certbot-nginx mode: "000755" owner: "ec2-user" group: "users" commands: install: command: "/root/install-sw.sh >> /var/log/install-sw.log 2>&1" ignoreErrors: "true" 00_dcv_install: files: "/root/install-dcv.sh": content: !Sub | #!/bin/bash mkdir -p /tmp/cfn cd /tmp/cfn # Update OS dnf update -q -y # DCV prereq: https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-installing-linux-prereq.html dnf groupinstall "Desktop" -y -q # Disable the Wayland protocol: https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-installing-linux-prereq.html#linux-prereq-wayland sed -i '/^\[daemon\]/a WaylandEnable=false' /etc/gdm/custom.conf # Microphone redirection: https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-installing-linux-server.html dnf install -q -y pulseaudio-utils # DCV: https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-installing-linux-server.html rpm --import https://d1uj6qtbmh3dt5.cloudfront.net/NICE-GPG-KEY curl -s -L -O https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-amzn2023-$(arch).tgz tar -xzf nice-dcv-amzn2023-$(arch).tgz && cd nice-dcv-*-amzn2023-$(arch) dnf install -q -y ./nice-dcv-server-*.rpm dnf install -q -y ./nice-dcv-web-viewer-*.rpm dnf install -q -y ./nice-xdcv-*.rpm # Printer redirection: https://docs.aws.amazon.com/dcv/latest/adminguide/manage-printer.html dnf install -q -y cups GROUP=$(cat /etc/cups/cups-files.conf | grep -oP "SystemGroup\s\K\w+") usermod -a -G $GROUP dcv systemctl enable cups # QUIC: https://docs.aws.amazon.com/dcv/latest/adminguide/enable-quic.html cp /etc/dcv/dcv.conf /etc/dcv/dcv.conf."`date +"%Y-%m-%d"`" sed -i "s/^#enable-quic-frontend=true/enable-quic-frontend=true/g" /etc/dcv/dcv.conf # Higher web client max resolution: https://docs.aws.amazon.com/dcv/latest/adminguide/config-param-ref.html sed -i "/^\[display/a web-client-max-head-resolution=(4096, 2160)" /etc/dcv/dcv.conf # Console session support sed -i "/^\[session-management\/automatic-console-session/a owner=\"ec2-user\"\nstorage-root=\"%home%\"" /etc/dcv/dcv.conf # systemctl daemon-reload systemctl enable dcv-virtual-session systemctl enable dcvserver rm -f ${!0} mode: "000740" owner: "root" group: "root" commands: install: command: "/root/install-dcv.sh > /var/log/install-dcv.log 2>&1" ignoreErrors: "true" 01_php_install: files: "/root/install-php.sh": content: !Sub | #!/bin/bash mkdir -p /tmp/cfn cd /tmp/cfn export PHP="${phpVersion}" case $PHP in none) ;; *) # Will install httpd dnf install -q -y ${phpVersion} dnf install -q -y ${phpVersion}-{cli,fpm,opcache} dnf install -q -y ${phpVersion}-{mysqlnd,pgsql,pdo,dba} dnf install -q -y ${phpVersion}-{xml,soap,ldap,intl} dnf install -q -y ${phpVersion}-{mbstring,bcmath,gd,enchant,tidy} # PHP.INI cp /etc/php.ini /etc/php.ini."`date +"%Y-%m-%d"`" # https://docs.moodle.org/405/en/Environment_-_max_input_vars sed -i "/^;max_input_vars/a max_input_vars=5000" /etc/php.ini sed -i 's/memory_limit =.*/memory_limit = 256M/' /etc/php.ini sed -i 's/upload_max_filesize =.*/upload_max_filesize = 2G/' /etc/php.ini sed -i 's/post_max_size =.*/post_max_size = 2G/' /etc/php.ini cp /etc/php-fpm.d/www.conf /etc/php-fpm.d/www.conf."`date +"%Y-%m-%d"`" # https://www.php.net/manual/en/class.sessionhandler.php if (! echo '${installApp}' | grep -q -i "moodle"); then # https://docs.moodle.org/405/en/PHP#PHP_Settings sed -i "s/^php_value\[session.save_handler/;&/" /etc/php-fpm.d/www.conf sed -i "/^;php_value\[session.save_handler/a php_value[session.save_handler] = redis" /etc/php-fpm.d/www.conf sed -i "s/^php_value\[session.save_path/;&/" /etc/php-fpm.d/www.conf sed -i '/^;php_value\[session.save_path/a php_value[session.save_path] = tcp://127.0.0.1:6379' /etc/php-fpm.d/www.conf fi # https://www.php.net/manual/en/opcache.configuration.php cp /etc/php.d/10-opcache.ini /etc/php.d/10-opcache.ini."`date +"%Y-%m-%d"`" sed -i '1 i\opcache.enable=1\n;opcache.jit=tracing\n;opcache.jit_buffer_size=128M' /etc/php.d/10-opcache.ini # https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.file-cache mkdir -p /var/www/.opcache chown apache:apache /var/www/.opcache sed -i 's/;opcache.file_cache=.*/;opcache.file_cache=\/var\/www\/.opcache/' /etc/php.d/10-opcache.ini # https://www.php.net/manual/en/install.pecl.pear.php dnf install -q -y php-devel php-pear gcc pear update-channels pecl update-channels # https://github.com/amazonlinux/amazon-linux-2023/issues/206 # php-apcu: https://github.com/krakjoe/apcu/blob/master/package.xml dnf install -q -y ${phpVersion}-pecl-apcu if (! php -m | grep -q apcu); then yes 'no' | pecl install --configureoptions 'enable-apcu-debug="no"' apcu if [ $? -eq 0 ]; then echo 'extension=apcu.so' > /etc/php.d/40-apcu.ini fi fi # php-igbinary: https://pecl.php.net/package/igbinary dnf install -q -y ${phpVersion}-pecl-igbinary if (! php -m | grep -q igbinary); then yes 'no' | pecl install igbinary if [ $? -eq 0 ]; then echo 'extension=igbinary.so' > /etc/php.d/25-igbinary.ini fi fi # php-msgpack: https://github.com/msgpack/msgpack-php/blob/master/package.xml dnf install -q -y ${phpVersion}-pecl-msgpack if (! php -m | grep -q msgpack); then yes 'no' | pecl install msgpack if [ $? -eq 0 ]; then echo 'extension=msgpack.so' > /etc/php.d/25-msgpack.ini fi fi # php-lzf: https://github.com/php/pecl-file_formats-lzf/blob/master/package.xml dnf install -q -y ${phpVersion}-lzf if (! php -m | grep -q lzf); then yes 'no' | pecl install --configureoptions 'enable-lzf-better-compression="no"' lzf if [ $? -eq 0 ]; then echo 'extension=lzf.so' > /etc/php.d/25-lzf.ini fi fi # php-zstd: https://github.com/kjdev/php-ext-zstd/blob/master/package.xml dnf install -q -y ${phpVersion}-zstd if (! php -m | grep -q zstd); then dnf install -q -y libzstd-devel yes 'no' | pecl install zstd if [ $? -eq 0 ]; then echo 'extension=zstd.so' > /etc/php.d/25-zstd.ini fi fi # php-imagick: https://github.com/Imagick/imagick/blob/master/package.xml dnf install -q -y ${phpVersion}-imagick dnf install -q -y ImageMagick dnf install -q -y ghostscript if (! php -m | grep -q imagick); then dnf install -q -y ImageMagick ImageMagick-devel yes 'no' | sudo pecl install imagick if [ $? -eq 0 ]; then echo 'extension=imagick.so' > /etc/php.d/25-imagick.ini fi fi # https://github.com/amazonlinux/amazon-linux-2023/issues/328 # php-redis: https://github.com/phpredis/phpredis/blob/develop/package.xml dnf install -q -y ${phpVersion}-pecl-redis6 if (! php -m | grep -q redis); then dnf install -q -y redis6-devel lz4-devel libzstd-devel yes 'no' | pecl install --configureoptions 'enable-redis-igbinary="yes" enable-redis-lzf="yes" enable-redis-zstd="yes" enable-redis-msgpack="yes" enable-redis-lz4="yes" with-liblz4="yes"' redis if [ $? -eq 0 ]; then echo 'extension=redis.so' > /etc/php.d/40-redis.ini fi fi # https://github.com/amazonlinux/amazon-linux-2023/issues/208 # php-memcached: https://github.com/php-memcached-dev/php-memcached/blob/master/package.xml dnf install -q -y ${phpVersion}-pecl-igbinary-devel dnf install -q -y ${phpVersion}-pecl-msgpack-devel dnf install -q -y ${phpVersion}-pecl-memcached if (! php -m | grep -q memcached); then dnf install -q -y memcached-devel libmemcached-awesome-devel zlib-devel cyrus-sasl-devel libevent-devel libzstd-devel yes 'no' | pecl install --configureoptions 'with-libmemcached-dir="yes" with-zlib-dir="yes" with-system-fastlz="no" enable-memcached-igbinary="yes" enable-memcached-msgpack="no" enable-memcached-json="yes" enable-memcached-protocol="yes" enable-memcached-sasl="yes" enable-memcached-session="yes"' memcached if [ $? -eq 0 ]; then echo 'extension=memcached.so' > /etc/php.d/40-memcached.ini fi fi # https://github.com/amazonlinux/amazon-linux-2023/issues/195 # php-zip: https://github.com/pierrejoye/php_zip/blob/master/package.xml dnf install -q -y ${phpVersion}-zip if (! php -m | grep -q zip); then dnf install -q -y libzip-devel yes 'no' | pecl install zip if [ $? -eq 0 ]; then echo 'extension=zip.so' > /etc/php.d/25-zip.ini fi fi # https://github.com/amazonlinux/amazon-linux-2023/issues/204 # libsodium: https://github.com/jedisct1/libsodium-php/blob/master/package.xml dnf install -q -y ${phpVersion}-sodium if (! php -m | grep -q sodium); then dnf install -q -y libsodium-devel yes 'no' | pecl install -f libsodium if [ $? -eq 0 ]; then echo 'extension=sodium.so' > /etc/php.d/25-sodium.ini fi fi # php-lz4: https://github.com/kjdev/php-ext-lz4 dnf install -q -y ${phpVersion}-lz4 if (! php -m | grep -q lz4); then cd /tmp/cfn dnf install -q -y git lz4-devel git clone --recursive --depth=1 https://github.com/kjdev/php-ext-lz4.git cd php-ext-lz4 phpize ./configure --with-lz4-includedir=/usr make make install if [ $? -eq 0 ]; then echo 'extension=lz4.so' > /etc/php.d/25-lz4.ini fi fi # php-timezonedb: https://github.com/php/pecl-datetime-timezonedb/blob/master/package.xml dnf install -q -y ${phpVersion}-timezonedb if (! php -m | grep -q timezonedb); then yes 'no' | pecl install -f timezonedb if [ $? -eq 0 ]; then echo 'extension=timezonedb.so' > /etc/php.d/25-timezonedb.ini fi fi # php-ssh2: https://github.com/php/pecl-networking-ssh2/blob/master/package.xml dnf install -q -y ${phpVersion}-ssh2 if (! php -m | grep -q ssh2); then sudo dnf install -y libssh2-devel yes 'no' | pecl install --configureoptions 'with-ssh2="yes"' ssh2 if [ $? -eq 0 ]; then echo 'extension=ssh2.so' > /etc/php.d/25-ssh2.ini fi fi dnf install -q -y ${phpVersion}-odbc unixODBC-devel # Microsoft ODBC driver for SQL Server : https://learn.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server # https://github.com/amazonlinux/amazon-linux-2023/issues/858 curl https://packages.microsoft.com/config/rhel/9/prod.repo | tee /etc/yum.repos.d/mssql-release.repo dnf remove -q -y unixODBC-utf16 unixODBC-utf16-devel #to avoid conflicts ACCEPT_EULA=Y dnf install -q -y msodbcsql18 ACCEPT_EULA=Y dnf install -q -y mssql-tools18 echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >> /root/.bashrc echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >> /home/ec2-user/.bashrc cd /opt/microsoft/msodbc*/ touch ACCEPT_EULA # Microsoft Drivers for PHP for SQL Server: https://learn.microsoft.com/en-us/sql/connect/php/microsoft-php-driver-for-sql-server # https://github.com/amazonlinux/amazon-linux-2023/issues/859 yes 'no' | pecl install sqlsrv if [ $? -eq 0 ]; then echo 'extension=sqlsrv.so' > /etc/php.d/20-sqlsrv.ini fi yes 'no' | pecl install pdo_sqlsrv if [ $? -eq 0 ]; then echo 'extension=pdo_sqlsrv.so' > /etc/php.d/30-pdo_sqlsrv.ini fi cd /tmp/cfn # https://github.com/amazonlinux/amazon-linux-2023/issues/371 # Composer: https://getcomposer.org/download/ dnf install -q -y composer if (! which composer); then curl -s -L -O https://getcomposer.org/installer export COMPOSER_HOME=/root && php installer --quiet mv ./composer.phar /usr/local/bin/composer fi systemctl enable php-fpm systemctl restart php-fpm ;; esac rm -f ${!0} mode: "000740" owner: "root" group: "root" "/root/index.php": content: | mode: "000644" owner: "root" group: "root" commands: install: command: "/root/install-php.sh > /var/log/install-php.log 2>&1" ignoreErrors: "true" 02_lamp_install: files: "/root/nginx-redirect": content: | # Redirect HTTP to HTTPS if not ACME HTTP-01 challenge, CloudFront CDN or Varnish/Vinyl Cache set $redirect_condition 1; if ($scheme != "http") { set $redirect_condition 0; } if ($http_x_amz_cf_id != "") { set $redirect_condition 0; } if ($http_x_varnish != "") { set $redirect_condition 0; } if ($request_uri ~ "^/\.well-known/") { set $redirect_condition 0; } if ($redirect_condition = 1) { return 301 https://$host$request_uri; } mode: "000640" owner: "root" group: "root" "/root/install-lamp.sh": content: !Sub | #!/bin/bash mkdir -p /tmp/cfn cd /tmp/cfn # Valkey (replace redis6) & Memcached dnf install -q -y valkey memcached systemctl enable --now valkey memcached # Web server export WEB="${webOption}" case $WEB in Apache) dnf install -q -y httpd mod_ssl mod_http2 mod_fcgid systemctl enable httpd # Enable HTTP/2 sed -i "/LoadModule /a Protocols h2 h2c http/1.1" /etc/httpd/conf.modules.d/10-h2.conf # TLS 1.2 and higher sed -i "/LoadModule /a SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1" /etc/httpd/conf.modules.d/00-ssl.conf # virtual host: for Certbot cat << EOF > /etc/httpd/conf.d/www.conf DocumentRoot /var/www/html RewriteEngine on RewriteCond %{REQUEST_URI} !/\.well\-known/?.* RewriteCond %{HTTP:X-AMZ-CF-ID} ^$ RewriteCond %{HTTP:X-VARNISH} ^$ RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent] EOF if (! echo "${phpVersion}" | grep -i -q "none"); then cp /root/index.php /var/www/html/ fi # Change permissions and ownership usermod -a -G apache ec2-user chown -R apache:apache /var/www/html chmod -R 2775 /var/www/html find /var/www/html -type d -exec sudo chmod 2775 {} \; find /var/www/html -type f -exec sudo chmod 0664 {} \; # Cloudwatch agent usermod -a -G apache cwagent systemctl restart httpd ;; Nginx) dnf remove -q -y httpd dnf install -q -y nginx systemctl enable nginx cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf."`date +"%Y-%m-%d"`" # enable sites-enabled and sites-available folders mkdir -p /etc/nginx/sites-enabled mkdir -p /etc/nginx/sites-available cp /etc/nginx/nginx.conf /etc/nginx/sites-available/default ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default # nginx.conf: comment out http site sed -i "37,54s/^/##/g" /etc/nginx/nginx.conf sed -i "/^http/a \ \include /etc/nginx/sites-enabled/*;" /etc/nginx/nginx.conf # build sites-available/default sed -i "1,36s/^/#/g" /etc/nginx/sites-available/default sed -i "80s/^#//g" /etc/nginx/sites-available/default cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default."`date +"%Y-%m-%d"`" # default: enable HTTPS sed -i "53,54s/^/#/g" /etc/nginx/sites-available/default sed -i "58,59s/^#//g" /etc/nginx/sites-available/default sed -i "81,83s/^/#/g" /etc/nginx/sites-available/default # dummy HTTPS cert mkdir -p /etc/pki/tls/nginx openssl req -x509 -newkey rsa:2048 -keyout /etc/pki/tls/nginx/key.pem -out /etc/pki/tls/nginx/cert.pem -sha256 -days 3650 -nodes -subj "/C=XX/ST=State/L=City/O=Company/OU=Dept/CN=Host" sed -i "s/ssl_certificate /#&/" /etc/nginx/sites-available/default sed -i "/#ssl_certificate /a \ \ssl_certificate \"/etc/pki/tls/nginx/cert.pem\";" /etc/nginx/sites-available/default sed -i "s/ssl_certificate_key /#&/" /etc/nginx/sites-available/default sed -i "/#ssl_certificate_key/a \ \ssl_certificate_key \"/etc/pki/tls/nginx/key.pem\";" /etc/nginx/sites-available/default sed -i "/#ssl_certificate_key/a \ \ssl_ciphers PROFILE=SYSTEM;" /etc/nginx/sites-available/default # set default_server sed -i "s/80;/80 default_server;/g" /etc/nginx/sites-available/default sed -i "s/http2;/http2 default_server;/g" /etc/nginx/sites-available/default # remove leading white spaces and commented lines sed -i "/^#/d" /etc/nginx/sites-available/default sed -i "s/^\s\s\s\s//" /etc/nginx/sites-available/default # redirect HTTP to HTTPS if not CloudFront sed -i '/^server/r /root/nginx-redirect' /etc/nginx/sites-available/default if (! echo "${phpVersion}" | grep -i -q "none"); then cp /root/index.php /usr/share/nginx/html/ fi # Change permissions and ownership usermod -a -G nginx ec2-user usermod -a -G nginx apache chown -R apache:nginx /usr/share/nginx/html chmod -R 2775 /usr/share/nginx/html find /usr/share/nginx/html -type d -exec sudo chmod 2775 {} \; find /usr/share/nginx/html -type f -exec sudo chmod 0664 {} \; # Cloudwatch agent usermod -a -G nginx cwagent systemctl restart nginx ;; none) dnf remove -q -y httpd ;; esac rm -f /root/nginx-redirect # Database export DB="${databaseOption}" case $DB in MySQL8.0) # https://dev.mysql.com/downloads/repo/yum/ # https://dev.mysql.com/doc/refman/8.0/en/checking-rpm-signature.html rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 # https://dev.mysql.com/doc/refman/8.0/en/linux-installation-yum-repo.html dnf install -q -y https://repo.mysql.com/mysql80-community-release-el9.rpm dnf install -q -y mysql-community-server if (ec2-metadata -t | grep -q large$); then sed -i '/^\[mysqld\]/a innodb_buffer_pool_size=1G' /etc/my.cnf mysqld --validate-config fi systemctl enable --now mysqld ;; MySQL8.4) # https://dev.mysql.com/downloads/repo/yum/ # https://dev.mysql.com/doc/refman/8.4/en/checking-rpm-signature.html rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 # https://dev.mysql.com/doc/refman/8.4/en/linux-installation-yum-repo.html dnf install -q -y https://repo.mysql.com/mysql84-community-release-el9.rpm dnf install -q -y mysql-community-server if (ec2-metadata -t | grep -q large$); then sed -i '/^\[mysqld\]/a innodb_buffer_pool_size=1G' /etc/my.cnf mysqld --validate-config fi systemctl enable --now mysqld ;; "MariaDB 10.11") # https://github.com/amazonlinux/amazon-linux-2023/issues/571 dnf install -q -y mariadb1011-server dnf install -q -y mariadb1011-backup if (ec2-metadata -t | grep -q large$); then sed -i '/^\[mysqld\]/a innodb_buffer_pool_size=1G' /etc/my.cnf.d/mariadb-server.cnf fi systemctl enable --now mariadb ;; "MariaDB 11.4") dnf install -q -y mariadb114-server dnf install -q -y mariadb114-backup if (ec2-metadata -t | grep -q large$); then sed -i '/^\[mysqld\]/a innodb_buffer_pool_size=1G' /etc/my.cnf.d/mariadb-server.cnf fi systemctl enable --now mariadb ;; PostgreSQL) PGSQL=$(dnf search postgresql*-server | grep ^postgresql | grep server$ | sort -r | head -n 1 | cut -d. -f1) dnf install -q -y $PGSQL /usr/bin/postgresql-setup --initdb systemctl enable --now postgresql ;; esac # NFS client dnf install -q -y amazon-efs-utils # https://github.com/amazonlinux/amazon-linux-2023/issues/277 # CodeDeploy agent: https://docs.aws.amazon.com/codedeploy/latest/userguide/codedeploy-agent-operations-install-linux.html dnf install -q -y ruby dnf install -q -y codedeploy-agent if [ ! -f /opt/codedeploy-agent/bin/codedeploy-agent ]; then curl -s -L -O https://aws-codedeploy-${AWS::Region}.s3.${AWS::Region}.amazonaws.com/latest/install chmod +x ./install ./install auto fi # install application stack? if (! echo '${installApp}' | grep -i -q "none"); then # create database and user if (which mysql); then systemctl start mysqld case '${installApp}' in WordPress) export DB=wordpress export DB_USER=wordpress ;; Moodle*) export DB=moodle export DB_USER=moodle ;; esac # database credentials in user home directory export DB_CREDS_FILE="/home/ec2-user/database-credentials" echo "Application Stack: ${installApp}" >> $DB_CREDS_FILE echo "Database: $DB" >> $DB_CREDS_FILE echo "User: $DB_USER" >>$DB_CREDS_FILE # generate strong password dnf install -q -y pwgen DB_USER_PW=$(pwgen -c -n -y -r "\/\'\"\`" 16) echo "User password = $DB_USER_PW" >> $DB_CREDS_FILE chown ec2-user:ec2-user $DB_CREDS_FILE chmod og-rwx $DB_CREDS_FILE # create database and user if (mysql --version | grep -q 8\.); then # MySQL8.x INITIAL_PW=$(sudo grep 'temporary password' /var/log/mysqld.log | grep -oE '[^ ]+$') mysql -u root -p$INITIAL_PW --connect-expired-password -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$DB_USER_PW';" mysql -u root -p$DB_USER_PW -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_USER_PW'; CREATE DATABASE $DB CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; GRANT ALL PRIVILEGES ON $DB.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" mysql -u root -p$DB_USER_PW -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$INITIAL_PW';" echo "Root password: $INITIAL_PW" >>$DB_CREDS_FILE else # MariaDB sudo mysql -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_USER_PW'; CREATE DATABASE $DB CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; GRANT ALL PRIVILEGES ON $DB.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" fi fi # Download application files mkdir -p /tmp/cfn cd /tmp/cfn case '${installApp}' in WordPress) curl -s -L -O https://wordpress.org/latest.tar.gz tar -xf latest.tar.gz # Allow CloudFront: https://developer.wordpress.org/advanced-administration/security/https/ sed -i "/Add any custom value/a \$_SERVER['HTTPS'] = 'on';" /tmp/cfn/wordpress/wp-config-sample.php # Install WP-CLI: https://make.wordpress.org/cli/handbook/guides/installing/ curl -s -L -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar chmod +x wp-cli.phar mv wp-cli.phar /usr/local/bin/wp case ${webOption} in Apache) # Allow permalinks: https://developer.wordpress.org/advanced-administration/server/web-server/httpd/ sed -i '156s/AllowOverride None/AllowOverride All/' /etc/httpd/conf/httpd.conf # Enable static content caching: https://developer.wordpress.org/advanced-administration/performance/optimizing-performance-with-caching/#leverage-browser-caching cat << EOF > /tmp/cfn/wordpress/.htaccess Header set Cache-Control "public, max-age=31536000" EOF rsync -r /tmp/cfn/wordpress/ /var/www/html/ chown apache:apache -R /var/www/html chmod g+rw -R /var/www/htmlp ;; Nginx) rsync -r /tmp/cfn/wordpress/ /usr/share/nginx/html/ chown apache:nginx -R /usr/share/nginx/html chmod g+rw -R /usr/share/nginx/html ;; esac ;; Moodle*) if (echo '${installApp}' | grep -q "Moodle 5.1"); then curl -s -L https://download.moodle.org/download.php/direct/stable501/moodle-latest-501.tgz -o moodle.tgz elif (echo '${installApp}' | grep -q "Moodle 5.0"); then curl -s -L https://download.moodle.org/download.php/direct/stable500/moodle-latest-500.tgz -o moodle.tgz else curl -s -L https://download.moodle.org/download.php/direct/stable405/moodle-latest-405.tgz -o moodle.tgz fi tar -xf moodle.tgz case ${webOption} in Apache) rsync -r /tmp/cfn/moodle/ /var/www/html/ chown apache:apache -R /var/www/html chmod g+rw -R /var/www/html # Moodle 5.1: https://moodledev.io/general/releases/5.1 if (echo '${installApp}' | grep -q "Moodle 5.1"); then sed -i "s/\/var\/www\/html/\/var\/www\/html\/public/g" /etc/httpd/conf/httpd.conf sed -i "s/\/var\/www\/html/\/var\/www\/html\/public/g" /etc/httpd/conf.d/ssl.conf sed -i "s/\/var\/www\/html/\/var\/www\/html\/public/g" /etc/httpd/conf.d/www.conf fi # Moodle data mkdir -p /var/www/moodledata chown apache:apache -R /var/www/moodledata chmod g+rw -R /var/www/moodledata # Moodle cron : https://docs.moodle.org/405/en/Cron echo "* * * * * /usr/bin/php /var/www/html/admin/cli/cron.php >/dev/null" > /home/ec2-user/moodle/cron ;; Nginx) rsync -r /tmp/cfn/moodle/ /usr/share/nginx/html/ chown apache:nginx -R /usr/share/nginx/html chmod g+rw -R /usr/share/nginx/html # Moodle 5.1: https://moodledev.io/general/releases/5.1 if (echo '${installApp}' | grep -q "Moodle 5.1"); then sed -i "s/\/usr\/share\/nginx\/html/\/usr\/share\/nginx\/html\/public/g" /etc/nginx/nginx.conf fi # Moodle data mkdir -p /usr/share/nginx/moodledata chown apache:nginx -R /usr/share/nginx/html chmod g+rw -R /usr/share/nginx/moodledata # Moodle cron : https://docs.moodle.org/405/en/Cron echo "* * * * * /usr/bin/php /usr/share/nginx/html/admin/cli/cron.php >/dev/null" > /home/ec2-user/moodle/cron ;; esac crontab -u apache /home/ec2-user/moodle/cron chown -R ec2-user:ec2-user /home/ec2-user/moodle ;; esac fi rm -f ${!0} mode: "000740" owner: "root" group: "root" commands: install: command: "/root/install-lamp.sh > /var/log/install-lamp.log 2>&1" ignoreErrors: "true" Properties: ImageId: !If [ useARM64, "{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.18-arm64}}", "{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.18-x86_64}}", ] InstanceType: !Ref instanceType IamInstanceProfile: !Ref instanceProfile KeyName: !Ref ec2KeyPair SubnetId: !Ref subnetID Monitoring: false DisableApiTermination: !If [enableProtection, true, false] EbsOptimized: true SecurityGroupIds: - !Ref sgEC2Instance - !If [hasCFprefix, !Ref sgCloudFrontIPv4, !Ref AWS::NoValue] - !If [hasCFprefix, !Ref sgCloudFrontIPv6, !Ref AWS::NoValue] BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeType: !Ref volumeType VolumeSize: !Ref volumeSize DeleteOnTermination: true Encrypted: true UserData: Fn::Base64: !Sub | #!/bin/bash mkdir -p /tmp/cfn cd /tmp/cfn # disable IPv6 during setup #sysctl -w net.ipv6.conf.all.disable_ipv6=1 #sysctl -w net.ipv6.conf.default.disable_ipv6=1 dnf clean all dnf install -q -y wget tmux unzip tar curl-minimal sed export CFN_INIT="/opt/aws/bin/cfn-init" $CFN_INIT -v --stack ${AWS::StackName} --resource ec2Instance --region ${AWS::Region} -c setup # Install desktop environment and DCV? if [ "${installDCV}" = "Yes" ]; then $CFN_INIT -v --stack ${AWS::StackName} --resource ec2Instance --region ${AWS::Region} -c dcv_install fi # Install PHP $CFN_INIT -v --stack ${AWS::StackName} --resource ec2Instance --region ${AWS::Region} -c php_install # Install rest of LAMP stack components $CFN_INIT -v --stack ${AWS::StackName} --resource ec2Instance --region ${AWS::Region} -c lamp_install # systemctl set-default multi-user.target systemctl daemon-reload systemctl enable dcv-post-reboot # enable back IPv6 #sysctl -w net.ipv6.conf.all.disable_ipv6=0 #sysctl -w net.ipv6.conf.default.disable_ipv6=0 sleep 1 && reboot Tags: - Key: Name Value: !Ref ec2Name - Key: StackName Value: !Ref AWS::StackName - Key: StackId Value: !Ref AWS::StackId - Key: GitHub Value: https://github.com/aws-samples/ec2-lamp-server elasticIP: Condition: useElasticIP Type: AWS::EC2::EIP Properties: Domain: vpc NetworkBorderGroup: !Ref AWS::Region InstanceId: !Ref ec2Instance Tags: - Key: StackName Value: !Ref AWS::StackName - Key: StackId Value: !Ref AWS::StackId - Key: Name Value: !Sub - "${AWS::StackName}-elasticIP-${UID}" - UID: !Select [ 3, !Split ["-", !Select [2, !Split ["/", !Ref AWS::StackId]]], ] - Key: GitHub Value: https://github.com/aws-samples/ec2-lamp-server alb: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Condition: createALB Properties: IpAddressType: !FindInMap [albIpTypeMapping, !Ref albIpAddressType, Value] Scheme: !Ref albScheme SecurityGroups: - !Ref albSecurityGroup - !If [hasCFprefix, !Ref sgCloudFrontIPv4, !Ref AWS::NoValue] - !If [hasCFprefix, !Ref sgCloudFrontIPv6, !Ref AWS::NoValue] Subnets: !Ref albSubnets Tags: - Key: StackName Value: !Ref AWS::StackName - Key: StackId Value: !Ref AWS::StackId - Key: GitHub Value: https://github.com/aws-samples/ec2-lamp-server albHttpListener: Type: AWS::ElasticLoadBalancingV2::Listener Condition: createHttpListener Properties: LoadBalancerArn: !Ref alb DefaultActions: - Type: forward TargetGroupArn: !Ref albTargetGroup Port: 80 Protocol: HTTP albHttpRedirectListener: Type: AWS::ElasticLoadBalancingV2::Listener Condition: createHttpRedirectListener Properties: LoadBalancerArn: !Ref alb Port: 80 Protocol: HTTP DefaultActions: - Type: "redirect" RedirectConfig: Protocol: "HTTPS" Port: 443 Host: "#{host}" Path: "/#{path}" Query: "#{query}" StatusCode: "HTTP_301" albHttpListenerRule: Type: AWS::ElasticLoadBalancingV2::ListenerRule Condition: createHttpRedirectListener Properties: ListenerArn: !Ref albHttpRedirectListener Priority: 10 Actions: - Type: forward TargetGroupArn: !Ref albTargetGroup Conditions: - Field: http-header HttpHeaderConfig: HttpHeaderName: X-Amz-Cf-Id RegexValues: [".+"] - Field: http-header HttpHeaderConfig: HttpHeaderName: Via RegexValues: [".+"] albHttpsListener: Type: AWS::ElasticLoadBalancingV2::Listener Condition: createHttpsListener Properties: LoadBalancerArn: !Ref alb Port: 443 Protocol: HTTPS Certificates: - CertificateArn: !Ref albCertificateArn SslPolicy: !Ref albSecurityPolicy DefaultActions: - Type: forward TargetGroupArn: !Ref albTargetGroup ListenerAttributes: - !If - sendHSTS - Key: routing.http.response.server.enabled Value: true - !Ref AWS::NoValue - !If - sendHSTS - Key: routing.http.response.strict_transport_security.header_value Value: !Ref albHstsHeaderValue - !Ref AWS::NoValue albTargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Condition: createALB Properties: HealthCheckEnabled: true HealthyThresholdCount: 2 Matcher: HttpCode: "200-302" IpAddressType: ipv4 Port: 443 Protocol: HTTPS ProtocolVersion: HTTP1 TargetGroupAttributes: - Key: stickiness.enabled Value: true - Key: stickiness.type Value: app_cookie - Key: stickiness.app_cookie.cookie_name Value: awscloud Tags: - Key: StackName Value: !Ref AWS::StackName - Key: StackId Value: !Ref AWS::StackId - Key: GitHub Value: https://github.com/aws-samples/ec2-lamp-server TargetType: instance Targets: - Id: !Ref ec2Instance Port: 443 VpcId: !Ref vpcID albSecurityGroup: Type: AWS::EC2::SecurityGroup Condition: createALB Properties: GroupDescription: ALB allow HTTP and HTTPS VpcId: !Ref vpcID SecurityGroupIngress: - !If - albAllowIPv4 - Description: HTTP (IPv4) IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 - !Ref AWS::NoValue - !If - albAllowIPv4 - Description: HTTPS (IPv4) IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 0.0.0.0/0 - !Ref AWS::NoValue - !If - albAllowIPv6 - Description: HTTP (IPv6) IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIpv6: ::/0 - !Ref AWS::NoValue - !If - albAllowIPv6 - Description: HTTPS (IPv6) IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIpv6: ::/0 - !Ref AWS::NoValue SecurityGroupEgress: - Description: Allow all outbound traffic (IPv4) IpProtocol: -1 CidrIp: 0.0.0.0/0 - Description: Allow all outbound traffic (IPv6) IpProtocol: -1 CidrIpv6: ::/0 Tags: - Key: StackName Value: !Ref AWS::StackName - Key: StackId Value: !Ref AWS::StackId - Key: Name Value: !Sub - "${AWS::StackName}-albSecurityGroup-${UID}" - UID: !Select [ 3, !Split ["-", !Select [2, !Split ["/", !Ref AWS::StackId]]], ] - Key: GitHub Value: https://github.com/aws-samples/ec2-lamp-server backupPlan: Type: AWS::Backup::BackupPlan Condition: createBackup Properties: BackupPlan: BackupPlanName: !Sub - "${AWS::StackName}-backupPlan-${UID}" - UID: !Select [ 3, !Split ["-", !Select [2, !Split ["/", !Ref AWS::StackId]]], ] BackupPlanRule: - RuleName: !Sub - "${AWS::StackName}-backupRule-${UID}" - UID: !Select [ 3, !Split ["-", !Select [2, !Split ["/", !Ref AWS::StackId]]], ] TargetBackupVault: !Ref backupVault ScheduleExpression: !Ref scheduleExpression ScheduleExpressionTimezone: !Ref scheduleExpressionTimezone Lifecycle: DeleteAfterDays: !Ref deleteAfterDays BackupPlanTags: { "StackName": !Ref AWS::StackName, "StackId": !Ref AWS::StackId, "GitHub": "https://github.com/aws-samples/ec2-lamp-server", } backupVault: Type: AWS::Backup::BackupVault Condition: createBackup UpdateReplacePolicy: Delete Properties: BackupVaultName: !Sub - "${AWS::StackName}-backupVault-${UID}" - UID: !Select [ 3, !Split ["-", !Select [2, !Split ["/", !Ref AWS::StackId]]], ] BackupVaultTags: { "StackName": !Ref AWS::StackName, "StackId": !Ref AWS::StackId, "GitHub": "https://github.com/aws-samples/ec2-lamp-server", } backupSelection: Type: AWS::Backup::BackupSelection Condition: createBackup Properties: BackupPlanId: !Ref backupPlan BackupSelection: IamRoleArn: !GetAtt backupRestoreRole.Arn Resources: - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/${ec2Instance}" SelectionName: !Sub - "${AWS::StackName}-backupSelection-${UID}" - UID: !Select [ 3, !Split ["-", !Select [2, !Split ["/", !Ref AWS::StackId]]], ] backupRestoreRole: Type: AWS::IAM::Role Condition: createBackup Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: backup.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: restore-EC2-instance-profile PolicyDocument: # https://docs.aws.amazon.com/aws-backup/latest/devguide/restoring-ec2.html Version: "2012-10-17" Statement: - Effect: Allow Action: - iam:PassRole Resource: !GetAtt instanceIamRole.Arn ManagedPolicyArns: - !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForBackup - !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForRestores Tags: - Key: StackName Value: !Ref AWS::StackName - Key: StackId Value: !Ref AWS::StackId - Key: GitHub Value: https://github.com/aws-samples/ec2-lamp-server cfVpcOrigin: Type: AWS::CloudFront::VpcOrigin Condition: cfVPCOrigin Properties: VpcOriginEndpointConfig: Arn: !If - createALB - !Sub arn:${AWS::Partition}:elasticloadbalancing:${AWS::Region}:${AWS::AccountId}:loadbalancer/${alb.LoadBalancerFullName} - !Sub arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/${ec2Instance} Name: !If - createALB - !Sub ${AWS::StackName}-${alb.LoadBalancerName} - !Sub ${AWS::StackName}-${ec2Instance} OriginProtocolPolicy: http-only OriginSSLProtocols: - TLSv1.2 Tags: - Key: StackName Value: !Ref AWS::StackName - Key: StackId Value: !Ref AWS::StackId - Key: GitHub Value: https://github.com/aws-samples/ec2-lamp-server cfDistribution: Type: AWS::CloudFront::Distribution Condition: createCloudFront DependsOn: ec2Instance Properties: DistributionConfig: Origins: - !If - cfVPCOrigin - DomainName: !If [ createALB, !GetAtt alb.DNSName, !GetAtt ec2Instance.PrivateDnsName, ] Id: !If [createALB, !GetAtt alb.LoadBalancerName, !Ref ec2Instance] VpcOriginConfig: VpcOriginId: !GetAtt cfVpcOrigin.Id - DomainName: !If [ createALB, !GetAtt alb.DNSName, !GetAtt ec2Instance.PublicDnsName, ] Id: !If [createALB, !GetAtt alb.LoadBalancerName, !Ref ec2Instance] CustomOriginConfig: OriginProtocolPolicy: http-only OriginSSLProtocols: - TLSv1.2 Enabled: true Comment: !Sub - "${AWS::StackName}-${UID}" - UID: !Select [ 3, !Split ["-", !Select [2, !Split ["/", !Ref AWS::StackId]]], ] HttpVersion: http2and3 ViewerCertificate: CloudFrontDefaultCertificate: true MinimumProtocolVersion: TLSv1.2_2025 DefaultCacheBehavior: AllowedMethods: !Split [",", "GET,HEAD,OPTIONS,PUT,PATCH,POST,DELETE"] CachedMethods: - "HEAD" - "GET" Compress: true CachePolicyId: "4cc15a8a-d715-48a4-82b8-cc0b614638fe" OriginRequestPolicyId: "b689b0a8-53d0-40ab-baf2-68738e2966ac" ResponseHeadersPolicyId: "67f7725c-6f97-4210-82d7-5512b31e9d03" TargetOriginId: !If [createALB, !GetAtt alb.LoadBalancerName, !Ref ec2Instance] ViewerProtocolPolicy: "redirect-to-https" Tags: - Key: StackName Value: !Ref AWS::StackName - Key: StackId Value: !Ref AWS::StackId - Key: GitHub Value: https://github.com/aws-samples/ec2-lamp-server Outputs: EC2console: Description: EC2 console Value: !Sub "https://${AWS::Region}.console.aws.amazon.com/ec2/home?region=${AWS::Region}#InstanceDetails:instanceId=${ec2Instance}" EC2instanceID: Description: EC2 Instance ID Value: !Ref ec2Instance EC2instanceConnect: Condition: createSgEIC Description: EC2 Instance Connect Value: !Sub "https://${AWS::Region}.console.aws.amazon.com/ec2-instance-connect/ssh?connType=standard&instanceId=${ec2Instance}&osUser=ec2-user&sshPort=22#/" EC2serialConsole: Description: EC2 Serial Console Value: !Sub "https://${AWS::Region}.console.aws.amazon.com/ec2-instance-connect/ssh?&connType=serial&instanceId=${ec2Instance}&serialPort=0#/" EC2iamRole: Description: EC2 IAM role Value: !Sub - "https://console.aws.amazon.com/iam/home#/roles/details/${role}" - role: !Select [1, !Split ["/", !GetAtt instanceIamRole.Arn]] SSMsessionManager: Description: SSM Session Manager (" sudo passwd ec2-user " to change login user password) Value: !Sub "https://${AWS::Region}.console.aws.amazon.com/systems-manager/session-manager/${ec2Instance}" DCVUrl: Condition: installDCV Description: DCV URL (login as ec2-user) Value: !If [ displayPublicIP, !Sub "https://${ec2Instance.PublicIp}:8443/?username=ec2-user ( dcv://ec2-user:@${ec2Instance.PublicIp} )", !Sub "https://${ec2Instance.PrivateIp}:8443/?username=ec2-user ( dcv://ec2-user:@${ec2Instance.PrivateIp} )", ] WebUrl: Description: Website Value: !If [ displayPublicIP, !Sub "https://${ec2Instance.PublicIp}", !Sub "https://${ec2Instance.PrivateIp}", ] WebminUrl: Condition: installWebmin Description: Webmin URL (login as ec2-user) Value: !If [ displayPublicIP, !Sub "https://${ec2Instance.PublicIp}:10000", !Sub "https://${ec2Instance.PrivateIp}:10000", ] CloudFrontUrl: Condition: createCloudFront Description: CloudFront distribution URL Value: !Sub "https://${cfDistribution.DomainName}" CloudFrontConsole: Condition: createCloudFront Description: CloudFront console Value: !Sub "https://console.aws.amazon.com/cloudfront/home#/distributions/${cfDistribution.Id}" AlbConsole: Condition: createALB Description: ALB Console Value: !Sub "https://${AWS::Region}.console.aws.amazon.com/ec2/home?region=${AWS::Region}#LoadBalancers:search=${alb}" AlbDnsName: Condition: createALB Description: ALB URL Value: !GetAtt alb.DNSName