Learn by building

From deployed to dependable.

Extend your Terraform and Ansible pipeline with DNS, optional trusted HTTPS, health checks, a recovery exercise, and verified cleanup.

1. The project and its boundaries

Build a small application server you can explain, change and repair. Terraform owns the network and VM. Ansible owns Nginx and its page. A reviewed pipeline coordinates them. HTTPS adds a trusted name; health checks tell you whether the app is responding.

Prerequisites: finish Start Here and the pipeline project. Reuse that exact project and backend. Do not create a duplicate server. Budget another 2–3 hours; certificate and DNS work is optional if you do not own a domain.

Not production: one public-subnet VM, broad outbound access, no HA, no automated certificate renewal, and no continuous alerting service. The demonstration page must contain no sensitive information. Infrastructure, IPv4, storage, DNS and CI may cost money.

Base pipeline ZIP · Extension files below are copied into that project's existing folders. No cloud resources are created by opening this tutorial.

2. Establish a known-good baseline

Complete the pipeline's administrator setup, review gate and first deployment. Record the instance ID privately. Confirm the permitted laptop can open the HTTP page and a second configure run completes. Do not bypass certificate or browser warnings.

Ubuntu / WSL · laptop, from the existing project root
aws sts get-caller-identity
terraform -chdir=terraform output
export TRANSFER_BUCKET="REPLACE-WITH-YOUR-PRIVATE-TRANSFER-BUCKET"
python scripts/inventory.py
ansible-playbook -i inventory.generated.json ansible/site.yml

Use the pipeline guide's configured virtual environment and short-lived login. Check the displayed account before proceeding. The generated inventory uses SSM; no guessed key path or SSH port is required.

3. Give the server a name you own

Use a separate hostname such as lab.YOUR-OWN-DOMAIN. At your domain's authoritative DNS provider, create one A record pointing it to the current Terraform public_ip output. Leave it DNS-only (not proxied) for this direct-server exercise. Do not change your production website's root record or nameservers.

Our examples use Contoso for project names only; you cannot obtain a certificate for contoso.com or domain.com unless you actually control it. Do not add an AAAA record: this project does not configure IPv6.

Windows PowerShell · laptop
Resolve-DnsName "REPLACE-WITH-YOUR-OWN-LAB-HOSTNAME" -Type A
Ubuntu / WSL · laptop
getent ahostsv4 REPLACE-WITH-YOUR-OWN-LAB-HOSTNAME

Expect your server's public IPv4, not a proxy address. DNS caches can take time. An automatically assigned EC2 address can change after stop/start; update your dedicated record then. This lab deliberately does not allocate an Elastic IP.

See the DNS guide for DNS concepts, and AWS's explanation of changing public addresses.

4. Obtain a certificate without opening the app to everyone

A certificate is a checked name badge. DNS-01 proves you control the name using a temporary TXT record. It does not require opening inbound port 80 to the Internet. Our server stays restricted to your /32.

Connect to your own instance using the EC2 console's Systems Manager connection, or an administrator-authorized Session Manager session. Interactive sessions require separate start-session permissions; the pipeline's command permissions may not include them. Ask your administrator rather than adding wildcard IAM access.

In that server session, install Ubuntu's packaged Certbot and request a certificate for your actual hostname:

Ubuntu server · interactive SSM session
sudo apt-get update
sudo apt-get install -y certbot
sudo certbot certonly --manual --preferred-challenges dns \
  --cert-name REPLACE-WITH-YOUR-OWN-LAB-HOSTNAME \
  -d REPLACE-WITH-YOUR-OWN-LAB-HOSTNAME
  1. Read the prompts and decide whether to accept the certificate authority's terms yourself.
  2. Create the exact TXT record and value Certbot shows at your authoritative DNS provider. Some DNS dashboards expect only the record prefix, not the full domain.
  3. Wait until the TXT value is publicly resolvable, then continue the terminal prompt. Do not guess a token.
  4. After successful issuance, remove only this challenge TXT record. Keep private keys on the VM; never copy them to Git or workflow logs.

Manual DNS certificates do not renew automatically. This is a short-lived lab. If keeping it, implement a reviewed DNS plugin with narrowly scoped credentials and reload Nginx after renewal; do not assume a timer can renew a manual challenge. Follow the Certbot manual guide and renewal guidance. Certificate names can appear in public transparency logs; use a non-sensitive lab hostname.

5. Review HTTPS access and configure Nginx

Copy this file into the existing terraform/https.tf. It adds port 443 only from the same approved /32. Keep the existing port 80 rule restricted; no public HTTP challenge rule is needed.

Commit the addition and run the existing deploy workflow. Review the saved plan: expect one HTTPS ingress rule, not VM replacement or a public CIDR. Apply only after review. The local alternative is a fresh reviewed plan in the same backend:

Ubuntu / WSL · laptop
terraform -chdir=terraform fmt
terraform -chdir=terraform validate
terraform -chdir=terraform plan -var-file=lab.tfvars -out=https.tfplan
# STOP: review every action and the account before applying.
terraform -chdir=terraform apply https.tfplan

Copy the two files below into ansible/secure.yml and ansible/nginx.conf.j2. This replaces the dedicated VM's full Nginx configuration, with a backup and validation before installation. Do not use it on a shared web server.

Ubuntu / WSL · laptop, existing project root
python scripts/inventory.py
ansible-playbook -i inventory.generated.json ansible/secure.yml \
  -e "site_domain=REPLACE-WITH-YOUR-OWN-LAB-HOSTNAME"

Run the same playbook again: expect no template changes. The base pipeline still runs site.yml; secure.yml is an explicitly separate local extension, not secretly integrated into CI. The HTTP endpoint remains for the original loopback acceptance check, so always type HTTPS for the browser exercise. There is no forced redirect or HSTS in this disposable example.

6. Verify from the outside and watch the server

From the permitted laptop, open your actual HTTPS hostname. Check that the browser trusts it. A green Ansible task inside the server cannot prove your laptop's DNS, firewall or certificate trust.

Windows PowerShell · laptop
curl.exe --fail --show-error --head https://REPLACE-WITH-YOUR-OWN-LAB-HOSTNAME/
Ubuntu / WSL · laptop
curl --fail --show-error --head https://REPLACE-WITH-YOUR-OWN-LAB-HOSTNAME/

Expect HTTP 200 and a trusted certificate. Never add -k or disable certificate verification to make this check pass.

Copy the health playbook into ansible/health.yml:

Ubuntu / WSL · laptop
ansible-playbook -i inventory.generated.json ansible/health.yml

In EC2, select this instance's Monitoring tab. Inspect basic CPU and network graphs and instance status checks. Basic EC2 metrics do not measure application success or filesystem usage; the health playbook checks the app and disk separately. It is a point-in-time inspection, not continuous monitoring or an alarm. Do not enable paid detailed monitoring or extra agents without reviewing costs.

For troubleshooting, use your interactive SSM server session:

Ubuntu server · inspect only
sudo systemctl status nginx --no-pager
sudo nginx -t
sudo journalctl -u nginx --since "15 minutes ago" --no-pager
sudo tail -n 20 /var/log/nginx/error.log
df -h /

Logs can reveal client addresses and request paths; redact them before sharing. See AWS's monitoring documentation.

7. Break one thing, then recover

On your own disposable lab only, deliberately stop Nginx. This creates a short service outage, not an infrastructure change. Do not run this in production.

Ubuntu server · deliberate lab outage
sudo systemctl stop nginx

The external HTTPS check should fail. Read service status; do not randomly change the firewall. From your laptop, rerun the base desired-state playbook:

Ubuntu / WSL · laptop
ansible-playbook -i inventory.generated.json ansible/site.yml
ansible-playbook -i inventory.generated.json ansible/health.yml

The service task should report changed and restart Nginx. The HTTPS configuration remains on disk. Repeat the external HTTPS check. Then rerun configuration: the service should already be running. This is desired-state repair, not high availability.

SymptomInspect first
SSM unavailableCorrect account/region, instance profile, agent and outbound connectivity
HTTPS times outYour current public /32, port 443 plan, VM address and service
Certificate mismatchExact hostname, DNS-only record, issued certificate name
Template validation failsCertificate paths and Nginx error; the old config should remain
Pipeline fails after applyThe VM may still be billed; repair configuration or review teardown

8. Clean up the whole experiment

  1. Save only redacted portfolio evidence, not certificates, state or plans.
  2. Run the pipeline's destroy mode, or the reviewed local destruction steps, with this same backend and all extension files present.
  3. Confirm the VM, root disk, HTTPS rule and dedicated network are gone.
  4. Remove only your dedicated DNS A record and leftover challenge TXT record. A dangling record can later point at someone else's server.
  5. Inspect private transfer files and plan versions; separately review unused bootstrap storage and IAM with the administrator.
  6. Check delayed billing later. Deleting a repository or stopping the VM is not full cleanup.

Download the evidence template. Explain one tradeoff: why this lab avoids a load balancer, why the manual certificate is temporary, or why health checks are not automatic recovery.

9. Sources and validation scope

Documentation reviewed October 2, 2026. The website's file and link checks are not evidence of a live AWS deployment. This extension has not been run against an AWS account here. Verify it in your authorized sandbox before relying on it.

Need help? Troubleshoot connections, automation and costs.