27 - Combined Setup (Certificate + Server)
Working Code:
terraform/exercise-27-combined-certificate/
The Problem: Generating cert → SCP to server → configure Nginx → restart... too much manual work.
The Solution: Use Terraform dependencies and cloud-init to do it all in one terraform apply.
Objective
One command that:
- Generates certificate (ACME)
- Spawns server (HCloud)
- Injects cert during boot (Cloud-Init)
- Configures Nginx
How-to
1. Generate Certificate
hcl
resource "acme_certificate" "wildcard" {
# ... (Same as Ex 25)
}2. Pass to Cloud-Init
hcl
user_data = templatefile("cloud-init.yaml", {
cert_pem = acme_certificate.wildcard.certificate_pem
key_pem = acme_certificate.wildcard.private_key_pem
})3. Cloud-Init Writes Files
yaml
write_files:
- path: /etc/ssl/certs/fullchain.pem
content: ${cert_pem}
- path: /etc/ssl/private/privkey.pem
content: ${key_pem}
- path: /etc/nginx/sites-available/default
content: |
server {
listen 443 ssl;
ssl_certificate /etc/ssl/certs/fullchain.pem;
ssl_certificate_key /etc/ssl/private/privkey.pem;
}
runcmd:
- systemctl restart nginxVerification
- Apply configuration:
terraform apply - Test HTTP redirects to HTTPS:
curl http://g2.sdi.hdm-stuttgart.cloud— expect301 Moved Permanently - Test HTTPS with staging cert (skip CA verification):
curl -k https://g2.sdi.hdm-stuttgart.cloud - Verify certificate in browser — expect warning with staging cert, inspect to confirm both SANs present
- Switch to production ACME URL in Exercise 25 and re-apply if staging works
Problems & Learnings
Common Issues
curl https://...will fail with a certificate error when using staging — this is expected. Usecurl -kto bypass or test in a browser.- The ACME certificate must be generated before the server is created, as the certificate content is passed to cloud-init. Terraform handles this ordering automatically via resource dependencies.
Key Takeaways
- HTTP → HTTPS redirect is configured via Nginx;
curlagainst port 80 returns a 301. - Staging certificates are functionally identical to production for testing the full chain — only browser/system trust differs.