├── files ├── foundry │ ├── AWS.json │ ├── foundry.service │ └── options.json ├── ddns │ ├── google │ │ ├── ddclient.conf │ │ └── ddclient_webserver.conf │ └── namecheap │ │ ├── ddclient.conf │ │ └── ddclient_webserver.conf ├── certbot │ └── reboot_certbot.sh └── nginx │ ├── drop │ ├── foundryvtt.conf │ └── foundryvtt_webserver.conf ├── scripts ├── amazon │ ├── hosted_zone_id.sh │ └── dynamic_dns.sh ├── namecheap │ └── record_set.sh ├── global │ ├── install.sh │ ├── nginx.sh │ ├── certbot.sh │ └── foundry.sh ├── google │ └── record_set.sh └── godaddy │ ├── dynamic_dns.sh │ └── record_set.sh ├── patches ├── node_v12.sh ├── certbot_cronjob.sh ├── sytemd_service.sh ├── node_v14.sh └── foundry_update.sh ├── readme.md ├── LICENSE └── cloudformation └── Foundry_Deployment.template /files/foundry/AWS.json: -------------------------------------------------------------------------------- 1 | { 2 | "accessKeyId": "ACCESSKEYIDHERE", 3 | "secretAccessKey": "SECRETACCESSKEYHERE", 4 | "region": "REGIONHERE" 5 | } -------------------------------------------------------------------------------- /files/ddns/google/ddclient.conf: -------------------------------------------------------------------------------- 1 | use=web, web=dynamicdns.park-your-domain.com/getip 2 | ssl=yes 3 | protocol=googledomains 4 | # Foundry domain info 5 | login=api_key 6 | password=api_secret 7 | subdomain.fqdn 8 | -------------------------------------------------------------------------------- /files/ddns/namecheap/ddclient.conf: -------------------------------------------------------------------------------- 1 | # Append this to /etc/ddclient.conf 2 | use=web, web=dynamicdns.park-your-domain.com/getip 3 | protocol=namecheap, 4 | server=dynamicdns.park-your-domain.com, 5 | login=fqdn, 6 | password=api_secret 7 | subdomain.fqdn -------------------------------------------------------------------------------- /files/certbot/reboot_certbot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo $PATH > /var/log/foundrycron/path.log 2>&1 4 | 5 | sleep 15 6 | certbot renew --nginx --no-self-upgrade --no-random-sleep-on-renew --post-hook "systemctl restart nginx" > /var/log/foundrycron/certbot_renew.log 2>&1 -------------------------------------------------------------------------------- /files/ddns/namecheap/ddclient_webserver.conf: -------------------------------------------------------------------------------- 1 | # Append this to /etc/ddclient.conf 2 | use=web, web=dynamicdns.park-your-domain.com/getip 3 | protocol=namecheap, 4 | server=dynamicdns.park-your-domain.com, 5 | login=fqdn, 6 | password=api_secret 7 | subdomain.fqdn, @.fqdn -------------------------------------------------------------------------------- /files/ddns/google/ddclient_webserver.conf: -------------------------------------------------------------------------------- 1 | use=web, web=dynamicdns.park-your-domain.com/getip 2 | ssl=yes 3 | protocol=googledomains 4 | # Foundry domain info 5 | login=api_key 6 | password=api_secret 7 | subdomain.fqdn 8 | # Webserver info 9 | login=webserver_user 10 | password=webserver_pass 11 | @.fqdn -------------------------------------------------------------------------------- /files/nginx/drop: -------------------------------------------------------------------------------- 1 | # turn off access and error logs for favicon and robots.txt when not found 2 | location = /favicon.ico { access_log off; log_not_found off; } 3 | location = /robots.txt { access_log off; log_not_found off; } 4 | location = /apple-touch-icon.png { access_log off; log_not_found off; } 5 | location = /apple-touch-icon-precomposed.png { access_log off; log_not_found off; } -------------------------------------------------------------------------------- /files/foundry/foundry.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Foundry VTT 3 | After=network.target 4 | 5 | [Service] 6 | Environment=NODE_PORT=30000 7 | WorkingDirectory=/home/foundry/foundry-install/resources/app/ 8 | Type=simple 9 | User=foundry 10 | ExecStart=/usr/bin/node /home/foundry/foundry-install/resources/app/main.js --dataPath=/foundrydata 11 | Restart=always 12 | RestartSec=10s 13 | 14 | [Install] 15 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /scripts/amazon/hosted_zone_id.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo yum install -y jq 4 | 5 | zone_id=`aws route53 list-hosted-zones | jq ".HostedZones[] | select(.Name==\"${fqdn}.\") | .Id" | cut -d / -f3 | cut -d '"' -f1` 6 | 7 | echo "zone_id=${zone_id}" >> /foundryssl/variables.sh 8 | 9 | sudo cp /aws-foundry-ssl/scripts/amazon/dynamic_dns.sh /foundryssl/dynamic_dns.sh 10 | crontab -l | { cat; echo "@reboot /foundryssl/dynamic_dns.sh > /dev/null"; } | crontab - 11 | crontab -l | { cat; echo "*/10 * * * * /foundryssl/dynamic_dns.sh > /dev/null"; } | crontab - -------------------------------------------------------------------------------- /files/foundry/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 30000, 3 | "upnp": true, 4 | "fullscreen": false, 5 | "hostname": "YOURSUBDOMAINHERE.YOURDOMAINHERE", 6 | "routePrefix": null, 7 | "sslCert": null, 8 | "sslKey": null, 9 | "awsConfig": "/foundrydata/Config/AWS.json", 10 | "dataPath": "/foundrydata", 11 | "proxySSL": true, 12 | "proxyPort": "443", 13 | "updateChannel": "release", 14 | "world": null, 15 | "turnListeningPort": 33478, 16 | "turnListeningIps": ["0.0.0.0"], 17 | "turnRelayIps": [], 18 | "turnMinPort": 49152, 19 | "turnMaxPort": 65535 20 | } -------------------------------------------------------------------------------- /patches/node_v12.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This patch is intended to be used on deployments created prior to MARCH 23, 2021 4 | # Only use this if you applied the Node v14.x patch and are experiencing stability issues 5 | # Do not use this if you deployed via template v1-7 or later 6 | # 7 | # NODE V12 REVERT: reverts node v14.x to v12.x 8 | 9 | # suspend foundry 10 | systemctl stop foundry 11 | 12 | # update existing packages 13 | yum -y update 14 | 15 | # unistall node add v12 to yum repository 16 | yum remove -y 'nodesource-release*' 'nodejs*' 17 | curl --silent --location https://rpm.nodesource.com/setup_12.x | sudo bash - 18 | yum clean all 19 | 20 | # install v12 21 | yum install -y nodejs 22 | systemctl start foundry -------------------------------------------------------------------------------- /scripts/namecheap/record_set.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source /foundryssl/variables.sh 3 | sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm 4 | sudo yum install -y ddclient 5 | 6 | if [[ ${webserver_bool} == "True" ]] 7 | then 8 | client_conf="ddclient_webserver.conf" 9 | else 10 | client_conf="ddclient.conf" 11 | fi 12 | 13 | sudo sed -i "s/api_secret/${api_secret}/g" /aws-foundry-ssl/files/ddns/namecheap/${client_conf} 14 | sudo sed -i "s/subdomain/${subdomain}/g" /aws-foundry-ssl/files/ddns/namecheap/${client_conf} 15 | sudo sed -i "s/fqdn/${fqdn}/g" /aws-foundry-ssl/files/ddns/namecheap/${client_conf} 16 | 17 | sudo cat /aws-foundry-ssl/files/ddns/namecheap/${client_conf} >> /etc/ddclient.conf 18 | 19 | sudo systemctl start ddclient 20 | sudo systemctl enable ddclient 21 | 22 | sudo ddclient --force -------------------------------------------------------------------------------- /files/nginx/foundryvtt.conf: -------------------------------------------------------------------------------- 1 | server { 2 | server_name YOURSUBDOMAINHERE.YOURDOMAINHERE; 3 | listen 80; 4 | client_max_body_size 300M; 5 | 6 | access_log /var/log/nginx/foundry/access.log; 7 | error_log /var/log/nginx/foundry/error.log; 8 | 9 | location / { 10 | proxy_set_header Host $host; 11 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 12 | proxy_set_header X-Forwarded-Proto $scheme; 13 | 14 | proxy_pass http://127.0.0.1:30000; 15 | 16 | proxy_http_version 1.1; 17 | proxy_set_header Upgrade $http_upgrade; 18 | proxy_set_header Connection "Upgrade"; 19 | } 20 | } -------------------------------------------------------------------------------- /files/nginx/foundryvtt_webserver.conf: -------------------------------------------------------------------------------- 1 | server { 2 | server_name YOURSUBDOMAINHERE.YOURDOMAINHERE; 3 | listen 80; 4 | client_max_body_size 300M; 5 | 6 | access_log /var/log/nginx/foundry/access.log; 7 | error_log /var/log/nginx/foundry/error.log; 8 | 9 | location / { 10 | proxy_set_header Host $host; 11 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 12 | proxy_set_header X-Forwarded-Proto $scheme; 13 | 14 | proxy_pass http://127.0.0.1:30000; 15 | 16 | proxy_http_version 1.1; 17 | proxy_set_header Upgrade $http_upgrade; 18 | proxy_set_header Connection "Upgrade"; 19 | } 20 | } 21 | server { 22 | listen 80; 23 | server_name www.YOURDOMAINHERE; 24 | return 301 http://YOURDOMAINHERE$request_uri; 25 | } 26 | server { 27 | listen 80 default_server; 28 | server_name YOURDOMAINHERE; 29 | index index.php index.html; 30 | } 31 | -------------------------------------------------------------------------------- /scripts/global/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | exec > /tmp/foundry-setup.log 2>&1 3 | set -x 4 | 5 | source /foundryssl/variables.sh 6 | source /foundryssl/variables_temp.sh 7 | 8 | case ${domain_registrar} in 9 | amazon) 10 | sleep 20s 11 | source /aws-foundry-ssl/scripts/amazon/hosted_zone_id.sh 12 | ;; 13 | godaddy) 14 | # set dns records and install dynamic dns 15 | source /aws-foundry-ssl/scripts/godaddy/record_set.sh 16 | ;; 17 | google) 18 | source /aws-foundry-ssl/scripts/google/record_set.sh 19 | ;; 20 | namecheap) 21 | source /aws-foundry-ssl/scripts/namecheap/record_set.sh 22 | ;; 23 | esac 24 | 25 | # install foundry 26 | source /aws-foundry-ssl/scripts/global/foundry.sh 27 | 28 | # install nginx 29 | source /aws-foundry-ssl/scripts/global/nginx.sh 30 | 31 | # set up certificates 32 | source /aws-foundry-ssl/scripts/global/certbot.sh 33 | 34 | # clean up install files 35 | # Do not do this during testing 36 | chmod 700 /tmp/foundry-setup.log 37 | sudo rm -r /aws-foundry-ssl 38 | sudo rm /foundryssl/variables_temp.sh 39 | 40 | #Restart Foundry So AWS.json is fully loaded 41 | sudo systemctl restart foundry 42 | -------------------------------------------------------------------------------- /scripts/google/record_set.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # grab variables 4 | source /foundryssl/variables.sh 5 | if [[ ${webserver_bool} == "True" ]] 6 | then 7 | client_conf="ddclient_webserver.conf" 8 | else 9 | client_conf="ddclient.conf" 10 | fi 11 | 12 | # install ddclient 13 | sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm 14 | sudo yum install -y ddclient 15 | 16 | # set ddclient config 17 | sudo sed -i "s/api_key/${api_key}/g" /aws-foundry-ssl/files/ddns/google/${client_conf} 18 | sudo sed -i "s/api_secret/${api_secret}/g" /aws-foundry-ssl/files/ddns/google/${client_conf} 19 | sudo sed -i "s/subdomain/${subdomain}/g" /aws-foundry-ssl/files/ddns/google/${client_conf} 20 | sudo sed -i "s/fqdn/${fqdn}/g" /aws-foundry-ssl/files/ddns/google/${client_conf} 21 | 22 | if [[ ${webserver_bool} == "True" ]] 23 | then 24 | sudo sed -i "s/webserver_user/${webserver_user}/g" /aws-foundry-ssl/files/ddns/google/${client_conf} 25 | sudo sed -i "s/webserver_pass/${webserver_pass}/g" /aws-foundry-ssl/files/ddns/google/${client_conf} 26 | fi 27 | 28 | sudo cat /aws-foundry-ssl/files/ddns/google/${client_conf} >> /etc/ddclient.conf 29 | 30 | # restart ddclient 31 | sudo systemctl start ddclient 32 | sudo systemctl enable ddclient 33 | 34 | sudo ddclient --force -------------------------------------------------------------------------------- /scripts/amazon/dynamic_dns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source /foundryssl/variables.sh 3 | 4 | # retrieve public ip 5 | public_ip="$(dig +short myip.opendns.com @resolver1.opendns.com)" 6 | 7 | # get IP for subdomain record 8 | aws_ip=`aws route53 list-resource-record-sets --hosted-zone-id ${zone_id} | jq ".ResourceRecordSets[] | select(.Name==\"${fqdn}\") | select(.Type==\"A\") | .ResourceRecords[] | .Value" | cut -d '"' -f2` 9 | 10 | # if dont match then create temp dns_block.json replacing domain_here and ip_here with values 11 | if [ "${public_ip}" != "${aws_ip}" ] 12 | then 13 | # change subdomain record 14 | aws route53 change-resource-record-sets --hosted-zone-id ${zone_id} --change-batch "{ \"Comment\": \"Dynamic DNS change\", \"Changes\": [ { \"Action\": \"UPSERT\", \"ResourceRecordSet\": { \"Name\": \"${subdomain}.${fqdn}\", \"Type\": \"A\", \"TTL\": 120, \"ResourceRecords\": [ { \"Value\": \"${public_ip}\" } ] } } ] }" 15 | if [ "${webserver_bool}" == "True" ] 16 | then 17 | aws route53 change-resource-record-sets --hosted-zone-id ${zone_id} --change-batch "{ \"Comment\": \"Dynamic DNS change\", \"Changes\": [ { \"Action\": \"UPSERT\", \"ResourceRecordSet\": { \"Name\": \"${fqdn}\", \"Type\": \"A\", \"TTL\": 120, \"ResourceRecords\": [ { \"Value\": \"${public_ip}\" } ] } } ] }" 18 | fi 19 | fi 20 | -------------------------------------------------------------------------------- /patches/certbot_cronjob.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This patch is intended to be used on deployments created prior to JANUARY 15TH, 2021 4 | # Do not use this if you deployed via template v1-6 or later 5 | # 6 | # CERTBOT CRONJOB PATCH: fixes the cronjob for certbot auto-renew 7 | 8 | SCRIPT_PATH=/foundrycron/reboot_certbot.sh 9 | CRON_PATH=/var/spool/cron/root 10 | 11 | yum install python-certbot-nginx -y 12 | mkdir /var/log/foundrycron /foundrycron 13 | 14 | # create certbot renewal script 15 | cat >> ${SCRIPT_PATH} < /var/log/foundrycron/path.log 2>&1 17 | sleep 15 18 | certbot renew --nginx --no-self-upgrade --no-random-sleep-on-renew --post-hook "systemctl restart nginx" > /var/log/foundrycron/certbot_renew.log 2>&1 19 | EOL 20 | chmod a+x ${SCRIPT_PATH} 21 | 22 | # change path in cron 23 | echo -e "PATH=/usr/bin:/bin:/usr/sbin\n\n$(cat ${CRON_PATH})" > ${CRON_PATH} 24 | 25 | # replace explicit renew with script 26 | sed -i "s|@reboot /usr/bin/certbot renew --quiet|@reboot ${SCRIPT_PATH}|" ${CRON_PATH} 27 | 28 | # replace scheduled renew 29 | sed -i "s|0 12 \* \* \* /usr/bin/certbot renew --quiet|0 12 \* \* \* certbot renew --nginx --no-self-upgrade --no-random-sleep-on-renew --post-hook \"systemctl restart nginx\" > /var/log/foundrycron/certbot_renew_daily.log 2>\&1|" ${CRON_PATH} -------------------------------------------------------------------------------- /scripts/global/nginx.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | source /foundryssl/variables.sh 3 | 4 | if [[ ${webserver_bool} == "True" ]] 5 | then 6 | foundry_file="foundryvtt_webserver.conf" 7 | else 8 | foundry_file="foundryvtt.conf" 9 | fi 10 | 11 | # install nginx 12 | sudo amazon-linux-extras install -y nginx1 13 | 14 | # configure nginx 15 | sudo mkdir /var/log/nginx/foundry 16 | sudo cp /aws-foundry-ssl/files/nginx/${foundry_file} /etc/nginx/conf.d/foundryvtt.conf 17 | sudo sed -i "s/YOURSUBDOMAINHERE/${subdomain}/g" /etc/nginx/conf.d/foundryvtt.conf 18 | sudo sed -i "s/YOURDOMAINHERE/${fqdn}/g" /etc/nginx/conf.d/foundryvtt.conf 19 | 20 | # start nginx 21 | sudo systemctl start nginx 22 | sudo systemctl enable nginx 23 | 24 | # configure foundry for nginx 25 | sudo sed -i "s/\"hostname\":.*/\"hostname\": \"${subdomain}\.${fqdn}\",/g" /foundrydata/Config/options.json 26 | sudo sed -i 's/"proxyPort":.*/"proxyPort": "80",/g' /foundrydata/Config/options.json 27 | 28 | # setup webserver 29 | if [[ ${webserver_bool} == "True" ]] 30 | then 31 | # copy webserver files 32 | git clone https://github.com/zkkng/foundry-website.git /foundry-website 33 | sudo cp -rf /foundry-website/* /usr/share/nginx/html 34 | 35 | # give ec2-user permissions 36 | sudo chown ec2-user -R /usr/share/nginx/html 37 | sudo chmod 755 -R /usr/share/nginx/html 38 | 39 | # clean up install files 40 | sudo rm -r /foundry-website 41 | fi 42 | 43 | systemctl restart nginx -------------------------------------------------------------------------------- /scripts/godaddy/dynamic_dns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # grab variables 4 | source /foundryssl/variables.sh 5 | 6 | # retrieve public ip 7 | public_ip="$(dig +short myip.opendns.com @resolver1.opendns.com)" 8 | 9 | # retrieve dns data of the subdomain from godaddy 10 | dns_data=`curl -s -X GET -H "Authorization: sso-key ${api_key}:${api_secret}" "https://api.godaddy.com/v1/domains/${fqdn}/records/A/${subdomain}"` 11 | godaddy_ip=`echo ${dns_data} | cut -d ',' -f 1 | tr -d '"' | cut -d ":" -f 2` 12 | 13 | # compare current public ip with godaddy record and replace if different 14 | if [ "${public_ip}" != "${godaddy_ip}" ] && [ "${public_ip}" != "" ] 15 | then 16 | if [ "${webserver_bool}" == "True" ] 17 | then 18 | # update the @ record (domain) 19 | curl -X PUT -H "Authorization: sso-key ${api_key}:${api_secret}" "https://api.godaddy.com/v1/domains/${fqdn}/records/A/@" -H "Content-Type: application/json" -H "Accept: application/json" -d "[{\"data\": \"${public_ip}\", \"name\": \"@\", \"ttl\": 600, \"type\": \"A\"}]" 20 | fi 21 | # update the A record (subdomain) 22 | curl -X PUT -H "Authorization: sso-key ${api_key}:${api_secret}" "https://api.godaddy.com/v1/domains/${fqdn}/records/A/${subdomain}" -H "Content-Type: application/json" -H "Accept: application/json" -d "[{\"data\": \"${public_ip}\", \"name\": \"${subdomain}\", \"ttl\": 600, \"type\": \"A\"}]" 23 | else 24 | echo "Dynamic DNS: Public IP (${public_ip}) and GoDaddy IP (${godaddy_ip}) are the same. No changes made" 25 | fi -------------------------------------------------------------------------------- /scripts/global/certbot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source /foundryssl/variables.sh 3 | 4 | # install epel 5 | sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm 6 | 7 | # install certbot 8 | sudo yum install -y certbot python2-certbot-nginx 9 | 10 | # install certificates 11 | sudo certbot --agree-tos -n --nginx -d ${subdomain}.${fqdn} -m ${email} --no-eff-email 12 | 13 | # install certificates for optional webserver 14 | if [[ ${webserver_bool} == 'True' ]]; then 15 | sudo certbot --agree-tos -n --nginx -d ${fqdn},www.${fqdn} -m ${email} --no-eff-email 16 | fi 17 | 18 | # install nginx certbot plugin 19 | sudo yum install -y python-certbot-nginx 20 | 21 | # configure to autorenew certs 22 | crontab -l | { cat; echo "@reboot /foundrycron/reboot_certbot.sh"; } | crontab - 23 | crontab -l | { cat; echo "0 12 * * * certbot renew --nginx --no-self-upgrade --no-random-sleep-on-renew --post-hook \"systemctl restart nginx\" > /var/log/foundrycron/certbot_renew_daily.log 2>&1"; } | crontab - 24 | echo -e "PATH=/usr/bin:/bin:/usr/sbin\n\n$(cat /var/spool/cron/root)" > /var/spool/cron/root 25 | 26 | sudo cp /aws-foundry-ssl/files/certbot/reboot_certbot.sh /foundrycron/reboot_certbot.sh 27 | 28 | sudo sed -i -e "s|location / {|include conf.d/drop;\n\n\tlocation / {|g" /etc/nginx/conf.d/foundryvtt.conf 29 | sudo cp /aws-foundry-ssl/files/nginx/drop /etc/nginx/conf.d/drop 30 | sudo systemctl restart nginx 31 | 32 | # configure foundry to use ssl 33 | sudo sed -i 's/"proxyPort":.*/"proxyPort": "443",/g' /foundrydata/Config/options.json 34 | sudo sed -i 's/"proxySSL":.*/"proxySSL": true,/g' /foundrydata/Config/options.json -------------------------------------------------------------------------------- /scripts/godaddy/record_set.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # grab public ip address 4 | source /foundryssl/variables.sh 5 | instance_ip=$(dig +short myip.opendns.com @resolver1.opendns.com) 6 | 7 | # adds custom foundry subdomain to GoDaddy Host Records 8 | # curl -X PUT 9 | # -H "Authorization: sso-key ${api_key}:${api_secret}" "https://api.godaddy.com/v1/domains/${fqdn}/records/A/${subdomain}" 10 | # -H "Content-Type: application/json" 11 | # -H "Accept: application/json" 12 | # -d "[{\"data\": \"${instance_ip}\",\"name\": \"${subdomain}\", \"ttl\": 1800, \"type\": \"A\"}]" 13 | curl -X PUT -H "Authorization: sso-key ${api_key}:${api_secret}" "https://api.godaddy.com/v1/domains/${fqdn}/records/A/${subdomain}" -H "Content-Type: application/json" -H "Accept: application/json" -d "[{\"data\": \"${instance_ip}\",\"name\": \"${subdomain}\", \"ttl\": 1800, \"type\": \"A\"}]" 14 | 15 | if [[ "${webserver_bool}" == "True" ]] 16 | then 17 | # replace A record (domain) with ec2 instance IP 18 | # curl -X PUT -H "Authorization: sso-key ${api_key}:${api_secret}" "https://api.godaddy.com/v1/domains/${fqdn}/records/A/@" 19 | # -H "Content-Type: application/json" 20 | # -H "Accept: application/json" 21 | # -d "[{\"data\": \"${instanct_ip}\", \"name\": \"@\", \"ttl\": 600, \"type\": \"A\"}]" 22 | curl -X PUT -H "Authorization: sso-key ${api_key}:${api_secret}" "https://api.godaddy.com/v1/domains/${fqdn}/records/A/@" -H "Content-Type: application/json" -H "Accept: application/json" -d "[{\"data\": \"${instance_ip}\", \"name\": \"@\", \"ttl\": 600, \"type\": \"A\"}]" 23 | fi 24 | 25 | 26 | # install dynamic dns as cron job 27 | sudo cp /aws-foundry-ssl/scripts/godaddy/dynamic_dns.sh /foundryssl/dynamic_dns.sh 28 | crontab -l | { cat; echo "@reboot /foundryssl/dynamic_dns.sh > /dev/null"; } | crontab - 29 | crontab -l | { cat; echo "*/10 * * * * /foundryssl/dynamic_dns.sh > /dev/null"; } | crontab - -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # AWS Foundry VTT Deployment with SSL Encryption 2 | 3 | _Deploys Foundry VTT with SSL encryption in AWS using CloudFormation (Beginner Friendly)_ 4 | 5 | 6 | ![GitHub](https://img.shields.io/github/license/cat-box/aws-foundry-ssl?style=flat-square) 7 | [![Maintenance](https://img.shields.io/maintenance/yes/2022?style=flat-square)](https://github.com/cat-box/aws-foundry-ssl/wiki/Patches) 8 | ![GitHub last commit](https://img.shields.io/github/last-commit/cat-box/aws-foundry-ssl?style=flat-square&color=blue) 9 | [![Reddit](https://img.shields.io/badge/u/auraofire-FF4500?style=flat-square&logo=reddit&logoColor=white)](https://www.reddit.com/user/auraofire) 10 | 11 | This is an upgraded version of the [original](https://www.reddit.com/r/FoundryVTT/comments/iurf7h/i_created_a_method_to_automatically_deploy_a/) beginner friendly automated AWS deployment Lupert and I worked on. We did some tinkering and it now handles setup and creation of AWS resources, in addition to fully configuring reverse proxy and SSL certificates. 12 | 13 | **tldr**: You can now use audio and video in Foundry to your heart's content! 14 | 15 | ### Head to the [**wiki**](https://github.com/cat-box/aws-foundry-ssl/wiki) for full instructions, and remember: READ EVERY. SINGLE. PAGE. 16 | --- 17 | ### New Features 18 | _New features which were added to this project will be automatically configured during setup._ 19 | 20 | 1. Domain name setup to point to your foundry server.
21 | **e.g.** https://dnd..com in your web browsers will show your foundry server. 22 | 23 | 2. Certificate for SSL encrypted server traffic, thereby enabling voice and video in foundry.
24 | **Note:** This includes automated certificate renewal so you never have to worry about certificate expirations. 25 | 26 | 3. Dynamic DNS to allow your domain to always point to the correct IP address even without a static IP address. 27 | 28 | 4. Systemd service to better handle the Foundry process. Previously, managing Foundry required a full reboot. 29 | 30 | It mainly provides two functions: 31 | 1. More elegant method of starting and stopping of Foundry. 32 | 2. Auto restart of Foundry in an event of crash. 33 | 34 | A [patch](https://github.com/cat-box/aws-foundry-ssl/wiki/Patches#rclocal-to-systemd-service) is available to those who deployed using earlier versions of this project. -------------------------------------------------------------------------------- /patches/sytemd_service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This patch is intended to be used on deployments created prior to OCT 23, 2020 4 | # 5 | # SYSTEMD SERVICE PATCH: Updates rc.local deployments to systemd service 6 | 7 | NC='\033[0m' 8 | RED='\033[0;31m' 9 | GREEN='\033[0;32m' 10 | YELLOW='\033[0;33m' 11 | 12 | # check if root 13 | if [ "$EUID" -ne 0 ] 14 | then 15 | echo -e "${RED}ERROR: Please run as root. (sudo su)${NC}" 16 | echo -e "${YELLOW}Patch unsuccessful. Quitting...${NC}" 17 | exit 18 | fi 19 | 20 | # check rc.local method 21 | if grep -Fq 'foundry' /etc/rc.local 22 | then 23 | echo -e "${YELLOW}Confirmed rc.local deployment method.${NC}" 24 | else 25 | echo -e "${RED}ERROR: Cannot detect rc.local launch method.${NC}" 26 | echo -e "${YELLOW}Patch unsuccessful. Quitting...${NC}" 27 | exit 28 | fi 29 | 30 | # terminate foundry process 31 | foundry_pid=`echo $(ps aux | grep '[f]oundry/resources/app/main.js') | cut -d ' ' -f2` 32 | kill -TERM ${foundry_pid} > /dev/null 2>&1 & 33 | sleep 5s 34 | 35 | ps -p ${foundry_pid} 36 | if [ "$?" -eq 0 ] 37 | then 38 | echo -e "${RED}ERROR: Unable to stop foundry process.${NC}" 39 | echo -e "${YELLOW}Patch unsuccessful. Quitting...${NC}" 40 | exit 41 | fi 42 | echo -e "${GREEN}Foundry PID ${foundry_pid} terminated.${NC}" 43 | 44 | # remove foundry start from rc.local 45 | sed -i "s|node /foundry/resources/app/main.js --dataPath=/foundrydata||" /etc/rc.local 46 | sed -i "s|node /foundry/resources/app/main.js --dataPath=/foundrydata||" /etc/rc.d/rc.local 47 | 48 | # install foundry as service 49 | sudo wget https://raw.githubusercontent.com/cat-box/aws-foundry-ssl/71192e27e9d178d2278634587544d494e9127ee3/files/foundry/foundry.service -P /etc/systemd/system 50 | sudo chmod 644 /etc/systemd/system/foundry.service 51 | sudo systemctl daemon-reload 52 | 53 | # enable service 54 | sudo systemctl enable foundry 55 | systemctl list-unit-files | grep enabled | grep foundry >&2 56 | foundry_systemd_status=$? 57 | if [ ${foundry_systemd_status} -eq 0 ] 58 | then 59 | # successfully enabled 60 | echo -e "${GREEN}Foundry service successfully enabled.${NC}" 61 | echo -e "${YELLOW}Restart the EC2 instance now to finish applying the patch.${NC}" 62 | else 63 | # service failed to enable 64 | echo -e "${RED}ERROR: Unable to enable foundry service.${NC}" 65 | echo -e "${YELLOW}Patch unsuccessful. Quitting...${NC}" 66 | exit 67 | fi -------------------------------------------------------------------------------- /scripts/global/foundry.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # grab variables 4 | source /foundryssl/variables_temp.sh 5 | source /foundryssl/variables.sh 6 | 7 | # install packages for foundry NOT NEEDED 8 | #sudo yum install -y nodejs 9 | #sudo yum install -y openssl-devel 10 | 11 | # download foundry from patreon link or google drive 12 | cd /home/foundry/foundry-install 13 | 14 | 15 | if [[ `echo ${foundry_download_link} | cut -d '/' -f3` == 'drive.google.com' ]]; then 16 | fileid=`echo ${foundry_download_link} | cut -d '/' -f6` 17 | while (( FS_Retry < 4 )) ; do 18 | sudo wget --quiet --save-cookies cookies.txt --keep-session-cookies --no-check-certificate "https://docs.google.com/uc?export=download&id=${fileid}" -O- | sed -rn 's/.*confirm=([0-9A-Za-z_]+).*/\1\n/p' > confirm.txt 19 | sudo wget --load-cookies cookies.txt -O foundry.zip 'https://docs.google.com/uc?export=download&id='${fileid}'&confirm='$( 100000000 )); then 23 | echo "Filesize seems about right! Proceeding." 24 | break 25 | else 26 | echo "Filesize looking too small. Retrying." 27 | ((FS_Retry++)) 28 | fi 29 | done 30 | else 31 | sudo wget -O foundry.zip "${foundry_download_link}" 32 | fi 33 | 34 | unzip -u foundry.zip 35 | rm -f foundry.zip 36 | 37 | # allow rwx in the Data folder only for ec2-user 38 | chown -R foundry:foundry /home/foundry/ /foundrydata 39 | find /foundrydata -type d -exec chmod 765 {} + 40 | find /foundrydata -type f -exec chmod 664 {} + 41 | 42 | # start foundry and add to boot 43 | sudo cp /aws-foundry-ssl/files/foundry/foundry.service /etc/systemd/system/foundry.service 44 | sudo chmod 644 /etc/systemd/system/foundry.service 45 | sudo systemctl daemon-reload 46 | sudo systemctl start foundry 47 | sudo systemctl enable foundry 48 | 49 | 50 | # configure foundry aws json file 51 | F_DIR='/foundrydata/Config/' 52 | echo "Start time: $(date +%s)" 53 | while (( Edit_Retry < 45 )) ; do 54 | if [ -d $F_DIR ]; then 55 | echo "Directory found time: $(date +%s)" 56 | sudo cp /aws-foundry-ssl/files/foundry/options.json /foundrydata/Config/options.json 57 | sudo cp /aws-foundry-ssl/files/foundry/AWS.json /foundrydata/Config/AWS.json 58 | sudo sed -i "s|ACCESSKEYIDHERE|${access_key_id}|g" /foundrydata/Config/AWS.json 59 | sudo sed -i "s|SECRETACCESSKEYHERE|${secret_access_key}|g" /foundrydata/Config/AWS.json 60 | sudo sed -i "s|REGIONHERE|${region}|g" /foundrydata/Config/AWS.json 61 | sudo sed -i 's|"awsConfig":.*|"awsConfig": "/foundrydata/Config/AWS.json",|g' /foundrydata/Config/options.json 62 | break 63 | else 64 | echo echo "Directory not found time: $(date +%s)" 65 | ((Edit_Retry++)) 66 | sleep 1s 67 | fi 68 | done -------------------------------------------------------------------------------- /patches/node_v14.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This patch is intended to be used on deployments created prior to MARCH 23, 2021 4 | # Do not use this if you deployed via template v1-7 or later 5 | # 6 | # NODE V14 PATCH: updates node v12.x to 14.x 7 | 8 | NC='\033[0m' 9 | RED='\033[0;31m' 10 | GREEN='\033[0;32m' 11 | YELLOW='\033[0;33m' 12 | 13 | # check deployment method 14 | check_deployment() { 15 | FILE=/etc/systemd/system/foundry.service 16 | if [ -f $FILE ] 17 | then 18 | # service deployment 19 | systemctl list-unit-files | grep enabled | grep foundry >&2 20 | foundry_systemd_status=$? 21 | if [ ${foundry_systemd_status} -eq 0 ] 22 | then 23 | echo "service" 24 | else 25 | echo "service_error" 26 | fi 27 | elif grep -Fq 'foundry' /etc/rc.local 28 | then 29 | # rc.local deployment 30 | echo "rc.local" 31 | else 32 | # cannot detect deployment method 33 | echo "neither" 34 | fi 35 | } 36 | 37 | # suspend foundry 38 | case "$(check_deployment)" in 39 | service) 40 | # service deployment 41 | systemctl stop foundry 42 | if systemctl is-active --quiet foundry 43 | then 44 | echo -e "${RED}ERROR_STOP: Unable to stop foundry service.${NC}" 45 | echo -e "${YELLOW}Patch unsuccessful. Quitting...${NC}" 46 | exit 47 | fi 48 | echo -e "${GREEN}Foundry service stopped.${NC}" 49 | ;; 50 | service_error) 51 | # service deployment disabled 52 | echo -e "${RED}ERROR_DEPLOYMENT: Detected service deployment but was not enabled.${NC}" 53 | echo -e "${YELLOW}Patch unsuccessful. Quitting...${NC}" 54 | exit 55 | ;; 56 | rc.local) 57 | # rc.local deployment 58 | foundry_pid=`echo $(ps aux | grep '[f]oundry/resources/app/main.js') | cut -d ' ' -f2` 59 | kill -TERM ${foundry_pid} > /dev/null 2>&1 & 60 | 61 | ps -p ${foundry_pid} 62 | if [ "$?" -eq 0 ] 63 | then 64 | echo -e "${RED}ERROR: Unable to stop foundry process.${NC}" 65 | echo -e "${YELLOW}Patch unsuccessful. Quitting...${NC}" 66 | exit 67 | fi 68 | echo -e "${GREEN}Foundry PID ${foundry_pid} terminated.${NC}" 69 | ;; 70 | neither) 71 | # cannot detect deployment method 72 | echo -e "${RED}ERROR_STOP: Unable to detect systemd or rc.local deployment method.${NC}" 73 | echo -e "${YELLOW}Patch unsuccessful. Quitting...${NC}" 74 | exit 75 | ;; 76 | esac 77 | 78 | # update existing packages 79 | yum -y update 80 | 81 | # unistall node add v14 to yum repository 82 | yum remove -y 'nodesource-release*' 'nodejs*' 83 | curl --silent --location https://rpm.nodesource.com/setup_14.x | sudo bash - 84 | yum clean all 85 | 86 | # install v14 87 | yum install -y nodejs 88 | 89 | # restart foundry 90 | case "$(check_deployment)" in 91 | service) 92 | # service deployment 93 | systemctl start foundry 94 | if systemctl is-active --quiet foundry 95 | then 96 | echo -e "${GREEN}Patch successfully finished.${NC}" 97 | echo -e "${YELLOW}If you are unable to access foundry, please wait 10 minutes before trying agin. If still unsuccessful, restart your EC2 Instance.${NC}" 98 | else 99 | echo -e "${RED}ERROR_START: Unable to restart foundry after patch.${NC}" 100 | echo -e "${YELLOW}Patch unsuccessful. Quitting...${NC}" 101 | exit 102 | fi 103 | ;; 104 | service_error) 105 | # service deployment disabled 106 | echo -e "${RED}ERROR_DEPLOYMENT: Detected service deployment but was not enabled.${NC}" 107 | echo -e "${YELLOW}Patch unsuccessful. Quitting...${NC}" 108 | exit 109 | ;; 110 | rc.local) 111 | # rc.local deployment 112 | echo -e "${GREEN}Patch finished. Please manually restart EC2 instance to finalize patch.${NC}" 113 | echo -e "${YELLOW}Quitting...${NC}" 114 | ;; 115 | neither) 116 | # cannot detect deployment method 117 | echo -e "${RED}ERROR_START: Unable to detect systemd or rc.local deployment method.${NC}" 118 | echo -e "${YELLOW}Patch unsuccessful. Quitting...${NC}" 119 | exit 120 | ;; 121 | esac -------------------------------------------------------------------------------- /patches/foundry_update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script is intended to be used on all deployments 4 | # 5 | # PREREQUISITE CHECKLIST: 6 | # [ ] Node.js v14 or later is installed 7 | # [ ] /foundrydata folder is backed up 8 | # 9 | # FOUNDRY UPDATE SCRIPT: updates FoundryVTT 10 | 11 | NC='\033[0m' 12 | RED='\033[0;31m' 13 | GREEN='\033[0;32m' 14 | YELLOW='\033[0;33m' 15 | 16 | # check if foundry download link was supplied 17 | if [ -z "$1" ] 18 | then 19 | echo -e "${RED}ERROR: Foundry download link not supplied." 20 | echo -e "${YELLOW}Update unsuccessful. Quitting...${NC}" 21 | exit 22 | fi 23 | 24 | # check if root 25 | if [ "$EUID" -ne 0 ] 26 | then 27 | echo -e "${RED}ERROR: Please run as root. (sudo su)${NC}" 28 | echo -e "${YELLOW}Update unsuccessful. Quitting...${NC}" 29 | exit 30 | fi 31 | 32 | # check node v14+ 33 | node_version=`echo $(node -v) | cut -d '.' -f1 | cut -d 'v' -f2` 34 | if [ $node_version -ge 14 ] 35 | then 36 | echo -e "${GREEN}Node.js confirmed running v14 or greater${NC}" 37 | else 38 | echo -e "${RED}ERROR: Foundry VTT 0.8 requires Node.js version 14 or greater." 39 | echo -e "${YELLOW}Please visit \e[4mhttps://github.com/cat-box/aws-foundry-ssl/wiki/Patches${YELLOW} to update Node.js first.${NC}" 40 | echo -e "${YELLOW}Update unsuccessful. Quitting...${NC}" 41 | exit 42 | fi 43 | 44 | # check deployment method 45 | check_deployment() { 46 | FILE=/etc/systemd/system/foundry.service 47 | if [ -f $FILE ] 48 | then 49 | # service deployment 50 | systemctl list-unit-files | grep enabled | grep foundry >&2 51 | foundry_systemd_status=$? 52 | if [ ${foundry_systemd_status} -eq 0 ] 53 | then 54 | echo "service" 55 | else 56 | echo "service_error" 57 | fi 58 | elif grep -Fq 'foundry' /etc/rc.local 59 | then 60 | # rc.local deployment 61 | echo "rc.local" 62 | else 63 | # cannot detect deployment method 64 | echo "neither" 65 | fi 66 | } 67 | 68 | # suspend foundry 69 | case "$(check_deployment)" in 70 | service) 71 | # service deployment 72 | systemctl stop foundry 73 | if systemctl is-active --quiet foundry 74 | then 75 | echo -e "${RED}ERROR_STOP: Unable to stop foundry service.${NC}" 76 | echo -e "${YELLOW}Update unsuccessful. Quitting...${NC}" 77 | exit 78 | fi 79 | echo -e "${GREEN}Foundry service stopped.${NC}" 80 | ;; 81 | service_error) 82 | # service deployment disabled 83 | echo -e "${RED}ERROR_DEPLOYMENT: Detected service deployment but was not enabled.${NC}" 84 | echo -e "${YELLOW}Update unsuccessful. Quitting...${NC}" 85 | exit 86 | ;; 87 | rc.local) 88 | # rc.local deployment 89 | foundry_pid=`echo $(ps aux | grep '[f]oundry/resources/app/main.js') | cut -d ' ' -f2` 90 | kill -TERM ${foundry_pid} > /dev/null 2>&1 & 91 | 92 | ps -p ${foundry_pid} 93 | if [ "$?" -eq 0 ] 94 | then 95 | echo -e "${RED}ERROR: Unable to stop foundry process.${NC}" 96 | echo -e "${YELLOW}Update unsuccessful. Quitting...${NC}" 97 | exit 98 | fi 99 | echo -e "${GREEN}Foundry PID ${foundry_pid} terminated.${NC}" 100 | ;; 101 | neither) 102 | # cannot detect deployment method 103 | echo -e "${RED}ERROR_STOP: Unable to detect systemd or rc.local deployment method.${NC}" 104 | echo -e "${YELLOW}Update unsuccessful. Quitting...${NC}" 105 | exit 106 | ;; 107 | esac 108 | 109 | # install 0.8x 110 | mv /foundry /foundryold_$(date +"%Y-%m-%d_%H-%M") 111 | mkdir /foundry 112 | foundry_download_link=$1 113 | if [[ `echo ${foundry_download_link} | cut -d '/' -f3` == 'drive.google.com' ]] 114 | then 115 | fileid=`echo ${foundry_download_link} | cut -d '/' -f6` 116 | sudo wget --quiet --save-cookies cookies.txt --keep-session-cookies --no-check-certificate "https://docs.google.com/uc?export=download&id=${fileid}" -O- | sed -rn 's/.*confirm=([0-9A-Za-z_]+).*/\1\n/p' > confirm.txt 117 | sudo wget --load-cookies cookies.txt -O foundry.zip 'https://docs.google.com/uc?export=download&id='${fileid}'&confirm='$( 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /cloudformation/Foundry_Deployment.template: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "Automated process to setup foundry exactly as the following guides dictate https://foundry-vtt-community.github.io/wiki/Self-Hosting-on-AWS/ and https://foundryvtt.com/article/nginx/. It also allows for optional snap-shots. Made by Lupert and Cat.", 4 | "Metadata": { 5 | "AWS::CloudFormation::Interface": { 6 | "ParameterGroups": [ 7 | { 8 | "Label": { 9 | "default": "Base Configuration" 10 | }, 11 | "Parameters": [ 12 | "AdminUserName", 13 | "AdminUserPW", 14 | "FoundryDownloadLink", 15 | "InstanceKey", 16 | "InstanceType", 17 | "AMI", 18 | "OptionalFixedIP", 19 | "OptionalSshAccess", 20 | "S3BucketName", 21 | "UseExistingBucket", 22 | "TakeSnapshots", 23 | "SnapshotFrequency" 24 | ] 25 | }, 26 | { 27 | "Label": { 28 | "default": "SSL Configuration" 29 | }, 30 | "Parameters": [ 31 | "FullyQualifiedDomainName", 32 | "SubdomainName", 33 | "DomainRegistrar", 34 | "WebServerBool", 35 | "Email" 36 | ] 37 | }, 38 | { 39 | "Label": { 40 | "default": "GoDaddy, Google, and NameCheap" 41 | }, 42 | "Parameters": [ 43 | "APIKey", 44 | "APISecret" 45 | ] 46 | }, 47 | { 48 | "Label": { 49 | "default": "Google Web Server" 50 | }, 51 | "Parameters": [ 52 | "GoogleAPIKey", 53 | "GoogleAPISecret" 54 | ] 55 | } 56 | ] 57 | } 58 | }, 59 | "Parameters": { 60 | "TakeSnapshots": { 61 | "Description": "Select True or False for whether you want to take snapshots of your instance. This is recommended but comes with a very minimal cost. This will retain 5 snapshots by default https://aws.amazon.com/ebs/pricing/", 62 | "Type": "String", 63 | "Default": "False", 64 | "AllowedValues": [ 65 | "True", 66 | "False" 67 | ] 68 | }, 69 | "UseExistingBucket": { 70 | "Description": "If you are not comfortable working with AWS, just leave this default. If you already have a bucket setup to work with Foundry set this option to True, and then enter the existing bucket's name as the value for parameter S3BucketName. The bucket must already have the correct policy set for this to work.", 71 | "Type": "String", 72 | "AllowedValues": [ 73 | "True", 74 | "False" 75 | ], 76 | "Default": "False" 77 | }, 78 | "FoundryDownloadLink": { 79 | "Description": "This will be your personal link to download Foundry. You can either use a Patreon link, or a Google Drive link.", 80 | "Type": "String" 81 | }, 82 | "S3BucketName": { 83 | "Description": "Name for S3 bucket that will be created. This must be globally unique across all of AWS, so be a little creative. Must be all lowercase letters or numbers, no spaces, and no symbols expect dash (-).", 84 | "ConstraintDescription": "This must be globally unique across all of AWS, so be a little creative. Must be all lowercase letters or numbers, no spaces, and no symbols expect dash (-).", 85 | "Type": "String", 86 | "AllowedPattern": "^[a-z|0-9]+(-[a-z|0-9]+)*$" 87 | }, 88 | "OptionalSshAccess": { 89 | "Description": "Will create a rulle allowing TCP port 22 inbound for the given IP Range. You MUST enter a range. For a single IP put /32 at the end. You will want to use your public IP (https://www.whatsmyip.org/). Example of a valid entry: 123.1.2.3/32. If you are not using this feature, leave this value blank and no SSH rule will be configured.", 90 | "Type": "String", 91 | "Default": "", 92 | "ConstraintDescription": "Must be a valid IP/CIDR range." 93 | }, 94 | "SnapshotFrequency": { 95 | "Description": "Decides how often you want to take snapshots. This value does not matter if you select False for TakeSnapshots", 96 | "Type": "String", 97 | "Default": "Weekly", 98 | "AllowedValues": [ 99 | "Daily", 100 | "Weekly" 101 | ] 102 | }, 103 | "OptionalFixedIP": { 104 | "Description": "Allows you to have a dedicated public IP for your instance. This prevents the IP from changing everytime you shutdown the instance. EIPs have a conditional cost. If an EIP is in use, it is free. If the EIP is not in use a minimal cost is applied.", 105 | "Type": "String", 106 | "Default": "False", 107 | "AllowedValues": [ 108 | "True", 109 | "False" 110 | ] 111 | }, 112 | "InstanceKey": { 113 | "Type": "AWS::EC2::KeyPair::KeyName", 114 | "Description": "An SSH keypair used to access your instance. This MUST be created manually.", 115 | "ConstraintDescription": "This MUST be created manually https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html#having-ec2-create-your-key-pair" 116 | }, 117 | "AdminUserPW": { 118 | "Type": "String", 119 | "NoEcho": "true", 120 | "Description": "Password for admin user. Have a minimum of 8 characters and a maximum of 128 characters include a minimum of three of the following mix of character types: uppercase, lowercase, numbers, and symbols.", 121 | "ConstraintDescription": "Have a minimum of 8 characters and a maximum of 128 characters include a minimum of three of the following mix of character types: uppercase, lowercase, numbers, and symbols." 122 | }, 123 | "AdminUserName": { 124 | "Type": "String", 125 | "Default": "AdminUser", 126 | "Description": "Username to be used for the Admin user." 127 | }, 128 | "InstanceType": { 129 | "Description": "WebServer EC2 instance type", 130 | "Type": "String", 131 | "Default": "t2.micro", 132 | "AllowedValues": [ 133 | "t2.micro", 134 | "t2.small", 135 | "t2.medium", 136 | "t2.large", 137 | "t3.micro", 138 | "m4.large", 139 | "m4.xlarge", 140 | "m4.2xlarge", 141 | "m4.4xlarge" 142 | ], 143 | "ConstraintDescription": "Must be a valid EC2 instance type." 144 | }, 145 | "AMI": { 146 | "Type": "AWS::SSM::Parameter::Value", 147 | "Description": "AWS managed ssm path for latest AMI in the CFN Deployment Region. Do not change this value.", 148 | "Default": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" 149 | }, 150 | "FullyQualifiedDomainName": { 151 | "Description": "This is the name of the domain you have purchased. (e.g. mywebsitename.com).", 152 | "Type": "String", 153 | "AllowedPattern": "^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9](?:\\.[a-zA-Z]{2,})+$" 154 | }, 155 | "SubdomainName": { 156 | "Description": "Name for the final URL to access foundry. Must be all lowercase, no letters, no spaces, and no symbols. (e.g. 'foundry' will become 'foundry.mywebsitename.com')", 157 | "Type": "String", 158 | "Default": "foundry", 159 | "AllowedPattern": "^[a-z]+$", 160 | "ConstraintDescription": "Must be all lowercase, no letters, no spaces, and no symbols." 161 | }, 162 | "APIKey": { 163 | "Description": "API Key from your domain registrar. If you are using Namecheap this will just be the same value you entered for FullyQualifiedDomainName.", 164 | "Type": "String" 165 | }, 166 | "APISecret": { 167 | "Description": "API Secret associated with the API Key above.", 168 | "Type": "String" 169 | }, 170 | "Email": { 171 | "Description": "A valid email address is required for letsencrypt certs.", 172 | "Type": "String" 173 | }, 174 | "DomainRegistrar": { 175 | "Description": "The domain registrar from which the domain was purchased from.", 176 | "Type": "String", 177 | "AllowedValues": [ 178 | "google", 179 | "godaddy", 180 | "namecheap", 181 | "amazon" 182 | ] 183 | }, 184 | "WebServerBool": { 185 | "Description": "Set this to true if you would like your instance to act as a web server for your base domain as well as host Foundry. Refer to guide section for additional DNS steps.", 186 | "Type": "String", 187 | "AllowedValues": [ 188 | "True", 189 | "False" 190 | ], 191 | "Default": "False" 192 | }, 193 | "GoogleAPIKey": { 194 | "Description": "This only matters if WebServer is set to true, and DomainRegistrar is set to google. If both of these conditions are not true ignore this parameter. Key for fully qualified domain name dynamic DNS.", 195 | "Default": "null", 196 | "Type": "String" 197 | }, 198 | "GoogleAPISecret": { 199 | "Description": "This only matters if WebServerBool is set to true, and DomainRegistrar is set to google. If both of these conditions are not true ignore this parameter. Secret for fully qualified domain name dynamic DNS.", 200 | "Default": "null", 201 | "Type": "String" 202 | } 203 | }, 204 | "Conditions": { 205 | "CreateSnapshots": { 206 | "Fn::Equals": [ 207 | { 208 | "Ref": "TakeSnapshots" 209 | }, 210 | "True" 211 | ] 212 | }, 213 | "HasSshIP": { 214 | "Fn::Not": [ 215 | { 216 | "Fn::Equals": [ 217 | "", 218 | { 219 | "Ref": "OptionalSshAccess" 220 | } 221 | ] 222 | } 223 | ] 224 | }, 225 | "CreateRoute53": { 226 | "Fn::Equals": [ 227 | { 228 | "Ref": "DomainRegistrar" 229 | }, 230 | "amazon" 231 | ] 232 | }, 233 | "CreateWebServer": { 234 | "Fn::Equals": [ 235 | { 236 | "Ref": "WebServerBool" 237 | }, 238 | "True" 239 | ] 240 | }, 241 | "Route53WebServer": { 242 | "Fn::And": [ 243 | { 244 | "Condition": "CreateRoute53" 245 | }, 246 | { 247 | "Condition": "CreateWebServer" 248 | } 249 | ] 250 | }, 251 | "NoExistingS3": { 252 | "Fn::Equals": [ 253 | { 254 | "Ref": "UseExistingBucket" 255 | }, 256 | "False" 257 | ] 258 | }, 259 | "CreateEIP": { 260 | "Fn::Equals": [ 261 | { 262 | "Ref": "OptionalFixedIP" 263 | }, 264 | "True" 265 | ] 266 | }, 267 | "DailyCheck": { 268 | "Fn::Equals": [ 269 | { 270 | "Ref": "SnapshotFrequency" 271 | }, 272 | "Daily" 273 | ] 274 | }, 275 | "WeeklyCheck": { 276 | "Fn::Equals": [ 277 | { 278 | "Ref": "SnapshotFrequency" 279 | }, 280 | "Weekly" 281 | ] 282 | }, 283 | "TakeSnapshots": { 284 | "Fn::Equals": [ 285 | { 286 | "Ref": "TakeSnapshots" 287 | }, 288 | "True" 289 | ] 290 | } 291 | }, 292 | "Resources": { 293 | "FoundryServerSG": { 294 | "Type": "AWS::EC2::SecurityGroup", 295 | "Properties": { 296 | "GroupDescription": "-", 297 | "SecurityGroupIngress": [ 298 | { 299 | "IpProtocol": "tcp", 300 | "FromPort": 30000, 301 | "ToPort": 30000, 302 | "CidrIp": "0.0.0.0/0" 303 | } 304 | ] 305 | } 306 | }, 307 | "SecurityGroupIngressVoice": { 308 | "Type": "AWS::EC2::SecurityGroupIngress", 309 | "Properties": { 310 | "GroupId": { 311 | "Fn::GetAtt": [ 312 | "FoundryServerSG", 313 | "GroupId" 314 | ] 315 | }, 316 | "CidrIp": "0.0.0.0/0", 317 | "FromPort": 80, 318 | "IpProtocol": "tcp", 319 | "ToPort": 80 320 | } 321 | }, 322 | "SecurityGroupIngressVoiceRange": { 323 | "Type": "AWS::EC2::SecurityGroupIngress", 324 | "Properties": { 325 | "GroupId": { 326 | "Fn::GetAtt": [ 327 | "FoundryServerSG", 328 | "GroupId" 329 | ] 330 | }, 331 | "CidrIp": "0.0.0.0/0", 332 | "FromPort": 49152, 333 | "IpProtocol": "tcp", 334 | "ToPort": 65535 335 | } 336 | }, 337 | "SecurityGroupIngressHTTP": { 338 | "Type": "AWS::EC2::SecurityGroupIngress", 339 | "Properties": { 340 | "GroupId": { 341 | "Fn::GetAtt": [ 342 | "FoundryServerSG", 343 | "GroupId" 344 | ] 345 | }, 346 | "CidrIp": "0.0.0.0/0", 347 | "FromPort": 33478, 348 | "IpProtocol": "tcp", 349 | "ToPort": 33478 350 | } 351 | }, 352 | "SecurityGroupIngressHTTPS": { 353 | "Type": "AWS::EC2::SecurityGroupIngress", 354 | "Properties": { 355 | "GroupId": { 356 | "Fn::GetAtt": [ 357 | "FoundryServerSG", 358 | "GroupId" 359 | ] 360 | }, 361 | "CidrIp": "0.0.0.0/0", 362 | "FromPort": 443, 363 | "IpProtocol": "tcp", 364 | "ToPort": 443 365 | } 366 | }, 367 | "SecurityGroupIngressSSH": { 368 | "Type": "AWS::EC2::SecurityGroupIngress", 369 | "Condition": "HasSshIP", 370 | "Properties": { 371 | "GroupId": { 372 | "Fn::GetAtt": [ 373 | "FoundryServerSG", 374 | "GroupId" 375 | ] 376 | }, 377 | "CidrIp": { 378 | "Ref": "OptionalSshAccess" 379 | }, 380 | "FromPort": 22, 381 | "IpProtocol": "tcp", 382 | "ToPort": 22 383 | } 384 | }, 385 | "Route53RSFoundry": { 386 | "Type": "AWS::Route53::RecordSet", 387 | "Condition": "CreateRoute53", 388 | "Properties": { 389 | "Name": { 390 | "Fn::Sub": "${SubdomainName}.${FullyQualifiedDomainName}" 391 | }, 392 | "Type": "A", 393 | "HostedZoneName": { 394 | "Fn::Sub": "${FullyQualifiedDomainName}." 395 | }, 396 | "ResourceRecords": [ 397 | { 398 | "Fn::GetAtt": [ 399 | "FoundryServer", 400 | "PublicIp" 401 | ] 402 | } 403 | ], 404 | "TTL": "60" 405 | } 406 | }, 407 | "Route53RSWeb": { 408 | "Type": "AWS::Route53::RecordSet", 409 | "Condition": "Route53WebServer", 410 | "Properties": { 411 | "Name": { 412 | "Ref": "FullyQualifiedDomainName" 413 | }, 414 | "Type": "A", 415 | "HostedZoneName": { 416 | "Fn::Sub": "${FullyQualifiedDomainName}." 417 | }, 418 | "ResourceRecords": [ 419 | { 420 | "Fn::GetAtt": [ 421 | "FoundryServer", 422 | "PublicIp" 423 | ] 424 | } 425 | ], 426 | "TTL": "60" 427 | } 428 | }, 429 | "Route53RSWebWWW": { 430 | "Type": "AWS::Route53::RecordSet", 431 | "Condition": "Route53WebServer", 432 | "Properties": { 433 | "Name": { 434 | "Fn::Sub": "www.${FullyQualifiedDomainName}" 435 | }, 436 | "Type": "CNAME", 437 | "HostedZoneName": { 438 | "Fn::Sub": "${FullyQualifiedDomainName}." 439 | }, 440 | "ResourceRecords": [ 441 | { 442 | "Ref": "FullyQualifiedDomainName" 443 | } 444 | ], 445 | "TTL": "60" 446 | } 447 | }, 448 | "FoundryBucket": { 449 | "Condition": "NoExistingS3", 450 | "Type": "AWS::S3::Bucket", 451 | "Properties": { 452 | "CorsConfiguration": { 453 | "CorsRules": [ 454 | { 455 | "AllowedHeaders": [ 456 | "*" 457 | ], 458 | "AllowedMethods": [ 459 | "GET", 460 | "POST", 461 | "HEAD" 462 | ], 463 | "MaxAge": "3000", 464 | "AllowedOrigins": [ 465 | "*" 466 | ] 467 | } 468 | ] 469 | }, 470 | "AccessControl": "PublicRead", 471 | "BucketName": { 472 | "Ref": "S3BucketName" 473 | } 474 | } 475 | }, 476 | "FoundryBucketPolicy": { 477 | "Condition": "NoExistingS3", 478 | "Type": "AWS::S3::BucketPolicy", 479 | "Properties": { 480 | "Bucket": { 481 | "Ref": "FoundryBucket" 482 | }, 483 | "PolicyDocument": { 484 | "Version": "2012-10-17", 485 | "Statement": [ 486 | { 487 | "Sid": "PublicReadGetObject", 488 | "Action": "s3:GetObject", 489 | "Effect": "Allow", 490 | "Resource": { 491 | "Fn::Sub": "arn:aws:s3:::${S3BucketName}/*" 492 | }, 493 | "Principal": "*" 494 | } 495 | ] 496 | } 497 | } 498 | }, 499 | "AdminUser": { 500 | "Type": "AWS::IAM::User", 501 | "Properties": { 502 | "LoginProfile": { 503 | "Password": { 504 | "Ref": "AdminUserPW" 505 | } 506 | }, 507 | "ManagedPolicyArns": [ 508 | "arn:aws:iam::aws:policy/AdministratorAccess" 509 | ], 510 | "UserName": { 511 | "Ref": "AdminUserName" 512 | } 513 | } 514 | }, 515 | "UserKey": { 516 | "Type": "AWS::IAM::AccessKey", 517 | "Properties": { 518 | "Status": "Active", 519 | "UserName": { 520 | "Ref": "FoundryUser" 521 | } 522 | } 523 | }, 524 | "FoundryUser": { 525 | "Type": "AWS::IAM::User", 526 | "Properties": { 527 | "Policies": [ 528 | { 529 | "PolicyName": "S3Foundry", 530 | "PolicyDocument": { 531 | "Version": "2012-10-17", 532 | "Statement": [ 533 | { 534 | "Sid": "ManageObjects", 535 | "Effect": "Allow", 536 | "Action": [ 537 | "s3:PutObject", 538 | "s3:GetObject", 539 | "s3:ListBucket", 540 | "s3:DeleteObject", 541 | "s3:PutObjectAcl" 542 | ], 543 | "Resource": [ 544 | { 545 | "Fn::Sub": "arn:aws:s3:::${S3BucketName}/*" 546 | }, 547 | { 548 | "Fn::Sub": "arn:aws:s3:::${S3BucketName}" 549 | } 550 | ] 551 | }, 552 | { 553 | "Sid": "ListBuckets", 554 | "Effect": "Allow", 555 | "Action": "s3:ListAllMyBuckets", 556 | "Resource": "*" 557 | } 558 | ] 559 | } 560 | } 561 | ] 562 | } 563 | }, 564 | "FoundryServer": { 565 | "Type": "AWS::EC2::Instance", 566 | "Metadata": { 567 | "AWS::CloudFormation::Init": { 568 | "config": { 569 | "packages": { 570 | "yum": { 571 | "git": [], 572 | "openssl-devel": [], 573 | "awslogs": [] 574 | } 575 | }, 576 | "users": { 577 | "foundry": { 578 | "uid": "182", 579 | "homeDir": "/home/foundry" 580 | } 581 | }, 582 | "files": { 583 | "/foundryssl/variables.sh": { 584 | "content": { 585 | "Fn::Join": [ 586 | "", 587 | [ 588 | "#!/bin/bash", 589 | "\n", 590 | "foundry_user=foundry", 591 | "\n", 592 | "foundry_home=/foundrydata", 593 | "\n", 594 | "region=", 595 | { 596 | "Ref": "AWS::Region" 597 | }, 598 | "\n", 599 | "domain_registrar=", 600 | { 601 | "Ref": "DomainRegistrar" 602 | }, 603 | "\n", 604 | "api_key=", 605 | { 606 | "Ref": "APIKey" 607 | }, 608 | "\n", 609 | "api_secret=", 610 | { 611 | "Ref": "APISecret" 612 | }, 613 | "\n", 614 | "fqdn=", 615 | { 616 | "Ref": "FullyQualifiedDomainName" 617 | }, 618 | "\n", 619 | "subdomain=", 620 | { 621 | "Ref": "SubdomainName" 622 | }, 623 | "\n", 624 | "email=", 625 | { 626 | "Ref": "Email" 627 | }, 628 | "\n", 629 | "webserver_bool=", 630 | { 631 | "Ref": "WebServerBool" 632 | }, 633 | "\n", 634 | "webserver_user=", 635 | { 636 | "Ref": "GoogleAPIKey" 637 | }, 638 | "\n", 639 | "webserver_pass=", 640 | { 641 | "Ref": "GoogleAPISecret" 642 | } 643 | ] 644 | ] 645 | }, 646 | "mode": "000700", 647 | "owner": "root", 648 | "group": "root" 649 | }, 650 | "/foundryssl/variables_temp.sh": { 651 | "content": { 652 | "Fn::Join": [ 653 | "", 654 | [ 655 | "#!/bin/bash", 656 | "\n", 657 | "foundry_download_link='", 658 | { 659 | "Ref": "FoundryDownloadLink" 660 | }, 661 | "'\n", 662 | "access_key_id=", 663 | { 664 | "Ref": "UserKey" 665 | }, 666 | "\n", 667 | "secret_access_key=", 668 | { 669 | "Fn::GetAtt": [ 670 | "UserKey", 671 | "SecretAccessKey" 672 | ] 673 | } 674 | ] 675 | ] 676 | }, 677 | "mode": "000700", 678 | "owner": "root", 679 | "group": "root" 680 | }, 681 | "/etc/awslogs/awslogs.conf": { 682 | "content": { 683 | "Fn::Join": [ 684 | "", 685 | [ 686 | "[general]\n", 687 | "state_file = /var/lib/awslogs/agent-state\n\n", 688 | "[/tmp/foundry-setup.log]\n", 689 | "file = /tmp/foundry-setup.log \n", 690 | "log_stream_name = {instance_id}\n", 691 | "initial_position = start_of_file\n", 692 | "log_group_name = Foundry-SSL-Logs\n" 693 | ] 694 | ] 695 | }, 696 | "mode": "000700", 697 | "owner": "root", 698 | "group": "root" 699 | }, 700 | "/etc/awslogs/awscli.conf": { 701 | "content": { 702 | "Fn::Join": [ 703 | "", 704 | [ 705 | "[plugins]\n", 706 | "cwlogs = cwlogs\n", 707 | "[/tmp/foundry-setup.log]\n", 708 | "[default]\n", 709 | "region = ", 710 | { 711 | "Ref": "AWS::Region" 712 | } 713 | ] 714 | ] 715 | }, 716 | "mode": "000700", 717 | "owner": "root", 718 | "group": "root" 719 | } 720 | }, 721 | "commands": { 722 | "A_Start_CW_Logger": { 723 | "command": "systemctl start awslogsd" 724 | }, 725 | "B_Install_Nodejs_14": { 726 | "command": "curl --silent --location https://rpm.nodesource.com/setup_14.x | sudo bash" 727 | }, 728 | "C_Install_Nodejs_14_cont": { 729 | "command": "yum install -y nodejs" 730 | }, 731 | "D_Make_Dirs": { 732 | "command": "mkdir -p /foundrycron /var/log/foundrycron /home/foundry/foundry-install /foundrydata" 733 | }, 734 | "E_Git_Clone": { 735 | "command": "git clone https://github.com/cat-box/aws-foundry-ssl.git", 736 | "cwd": "/" 737 | }, 738 | "F_Start_Install": { 739 | "command": "sh /aws-foundry-ssl/scripts/global/install.sh", 740 | "cwd": "/" 741 | }, 742 | "G_Give_Ec2_User_Access": { 743 | "command": "usermod -a -G foundry ec2-user" 744 | }, 745 | "H_CW_Logs_Retention": { 746 | "env": { "AWS_REGION": { "Ref": "AWS::Region" }}, 747 | "command": "aws --region $AWS_REGION logs put-retention-policy --log-group-name Foundry-SSL-Logs --retention-in-days 14" 748 | } 749 | } 750 | } 751 | } 752 | }, 753 | "CreationPolicy": { 754 | "ResourceSignal": { 755 | "Timeout": "PT25M" 756 | } 757 | }, 758 | "Properties": { 759 | "KeyName": { 760 | "Ref": "InstanceKey" 761 | }, 762 | "ImageId": { 763 | "Ref": "AMI" 764 | }, 765 | "InstanceType": { 766 | "Ref": "InstanceType" 767 | }, 768 | "IamInstanceProfile": { 769 | "Ref": "InstanceProfile" 770 | }, 771 | "SecurityGroupIds": [ 772 | { 773 | "Ref": "FoundryServerSG" 774 | } 775 | ], 776 | "Tags": [ 777 | { 778 | "Key": "Name", 779 | "Value": "FoundryServer" 780 | } 781 | ], 782 | "UserData": { 783 | "Fn::Base64": { 784 | "Fn::Join": [ 785 | "", 786 | [ 787 | "#!/bin/bash -x\n", 788 | "#Could not get this to work in cfn-init\n", 789 | "AWS_AVAIL_ZONE=$(curl http://169.254.169.254/latest/meta-data/placement/availability-zone)\n", 790 | "AWS_REGION=${AWS_AVAIL_ZONE::-1}\n", 791 | "AWS_INSTANCE_ID=$(curl http://169.254.169.254/latest/meta-data/instance-id)\n", 792 | "ROOT_VOLUME_IDS=$(aws ec2 describe-instances --region $AWS_REGION --instance-id $AWS_INSTANCE_ID --output text --query Reservations[0].Instances[0].BlockDeviceMappings[0].Ebs.VolumeId)\n", 793 | "aws ec2 create-tags --resources ${ROOT_VOLUME_IDS} --region $AWS_REGION --tags Key=Snapshot,Value=true\n", 794 | "# Install the files and packages from the metadata\n", 795 | "/opt/aws/bin/cfn-init -v ", 796 | " --stack ", 797 | { 798 | "Ref": "AWS::StackName" 799 | }, 800 | " --resource FoundryServer ", 801 | " --region ", 802 | { 803 | "Ref": "AWS::Region" 804 | }, 805 | "\n", 806 | "# Signal the status from cfn-init\n", 807 | "/opt/aws/bin/cfn-signal -e $? ", 808 | " --stack ", 809 | { 810 | "Ref": "AWS::StackName" 811 | }, 812 | " --resource FoundryServer ", 813 | " --region ", 814 | { 815 | "Ref": "AWS::Region" 816 | } 817 | ] 818 | ] 819 | } 820 | } 821 | }, 822 | "DependsOn": "UserKey" 823 | }, 824 | "InstanceRole": { 825 | "Type": "AWS::IAM::Role", 826 | "Properties": { 827 | "AssumeRolePolicyDocument": { 828 | "Version": "2012-10-17", 829 | "Statement": [ 830 | { 831 | "Effect": "Allow", 832 | "Principal": { 833 | "Service": [ 834 | "ec2.amazonaws.com" 835 | ] 836 | }, 837 | "Action": [ 838 | "sts:AssumeRole" 839 | ] 840 | } 841 | ] 842 | }, 843 | "Policies": [ 844 | { 845 | "PolicyName": "EC2Manager", 846 | "PolicyDocument": { 847 | "Version": "2012-10-17", 848 | "Statement": [ 849 | { 850 | "Sid": "VisualEditor0", 851 | "Effect": "Allow", 852 | "Action": [ 853 | "ec2:DescribeInstances", 854 | "ec2:CreateTags", 855 | "route53:ListResourceRecordSets", 856 | "route53:ChangeResourceRecordSets", 857 | "route53:ListHostedZones", 858 | "logs:CreateLogGroup", 859 | "logs:CreateLogStream", 860 | "logs:PutLogEvents", 861 | "logs:DescribeLogStreams", 862 | "logs:PutRetentionPolicy" 863 | ], 864 | "Resource": "*" 865 | } 866 | ] 867 | } 868 | } 869 | ] 870 | } 871 | }, 872 | "InstanceProfile": { 873 | "Type": "AWS::IAM::InstanceProfile", 874 | "Properties": { 875 | "Roles": [ 876 | { 877 | "Ref": "InstanceRole" 878 | } 879 | ] 880 | } 881 | }, 882 | "InstanceEIP": { 883 | "Condition": "CreateEIP", 884 | "Type": "AWS::EC2::EIP", 885 | "Properties": { 886 | "InstanceId": { 887 | "Ref": "FoundryServer" 888 | } 889 | } 890 | }, 891 | "SnapshotPolicy": { 892 | "Condition": "CreateSnapshots", 893 | "Type": "AWS::DLM::LifecyclePolicy", 894 | "Properties": { 895 | "State": "ENABLED", 896 | "Description": "Foundry Server snapshot policy", 897 | "ExecutionRoleArn": { 898 | "Fn::GetAtt": [ 899 | "InstanceRole", 900 | "Arn" 901 | ] 902 | }, 903 | "PolicyDetails": { 904 | "PolicyType": "EBS_SNAPSHOT_MANAGEMENT", 905 | "ResourceTypes": [ 906 | "VOLUME" 907 | ], 908 | "TargetTags": [ 909 | { 910 | "Key": "Snapshot", 911 | "Value": "true" 912 | } 913 | ], 914 | "Schedules": [ 915 | { 916 | "Fn::If": [ 917 | "DailyCheck", 918 | { 919 | "Name": "Snapshots", 920 | "CreateRule": { 921 | "CronExpression": "cron(0 12 * * ? *)" 922 | }, 923 | "RetainRule": { 924 | "Count": 5 925 | }, 926 | "CopyTags": true 927 | }, 928 | { 929 | "Name": "Snapshots", 930 | "CreateRule": { 931 | "CronExpression": "cron(0 12 ? * 2 *)" 932 | }, 933 | "RetainRule": { 934 | "Count": 5 935 | }, 936 | "CopyTags": true 937 | } 938 | ] 939 | } 940 | ] 941 | } 942 | } 943 | } 944 | } 945 | } --------------------------------------------------------------------------------