This repository holds code that demonstrates a progression of ever more sophisticated use cases for orchestrating Terraform. We use Temporal with golang for the orchestration.
This was orginally developed for a workshop delivered at PlatformCon 2025 (live in London and virtual) (will add links the the recordings when available).
This code orchestrates the provisioning and management of an AWS EC2 instance and then a Cloudflare DNS record for the newly created server. It does support multiple running environments with a very simple state file storage mechanism - it stores them in subdirectories under terraform-configs/*-terraform/state.
Please see the session recordings (coming soon) for more details.
- temporal cli installed
- terraform cli installed
- AWS CLI installed and configured with SSO profiles
- You will need a domain to which you can add DNS entries in Cloudflare
This project uses AWS SSO for authentication. Before running the application:
-
Authenticate with AWS SSO using one of your configured profiles:
aws sso login --profile AWSAdministratorAccess-269172689222 # OR aws sso login --profile corp-sso -
Set the AWS_PROFILE environment variable to specify which profile to use:
export AWS_PROFILE=AWSAdministratorAccess-269172689222 # OR export AWS_PROFILE=corp-sso
Note: If
AWS_PROFILEis not set, Terraform will use the default AWS credential chain (which may include your default profile or other configured credentials). -
Set Cloudflare environment variables:
export CLOUDFLARE_ZONE_ID="..." export CLOUDFLARE_API_TOKEN="..."
Important: AWS SSO sessions typically expire after a few hours. If you encounter authentication errors, re-authenticate with aws sso login --profile <your-profile>.
You will need three command windows.
In the first, run the Temporal service locally with temporal server start-dev. You will then find the Temporal UI at http://localhost:8233/
You will use the other two to perform the demos
git checkout demo1- Run the orchstration in the first terminal:
go run ./cmd/worker/main.go - Run the starter in the second terminal:
go run ./cmd/starter/main.go -action=create - To destroy
go run ./cmd/starter/main.go -action=destroy -environment=env-id - Show the workflow in the UI - just the two activity calls.
- Show the workflow code.
git checkout demo2- Run the orchstration:
go run ./cmd/worker/main.go - Run the starter:
go run ./cmd/starter/main.go -action=create -dns-approved=false- To approve
go run ./cmd/starter/main.go -action=approve -environment=env-id - To destroy
go run ./cmd/starter/main.go -action=destroy -environment=env-id
- To approve
- In the UI show the signal coming in.
- In the code show the selectors that allow for signals to come into the workflow
- Kill the orchestration while it's waiting on approval. Let the timer fire. Bring the orchestration back.
- Use the
shouldfailflag in the activity. If you do it for DNS you'll have the opportunity to point out that the AWS infra provisioning is not retried.
git checkout demo3- Run the orchstration:
go run ./cmd/worker/main.go - Run the starter:
go run ./cmd/starter/main.go -action=create -dns-approved=false- To approve
go run ./cmd/starter/main.go -action=approve -environment=env-id - To destroy
go run ./cmd/starter/main.go -action=destroy -environment=env-id
- To approve
- Show the timer
- Show the durability of the timer by running again and killing the orchstration right after the timer starts.
- Start a creation:
go run ./cmd/starter/main.go -action=create - Kill the orchstration before the AWS provisioning is finished.
- Restart the orchstration
- After the start to close timeout, it will retry which doesn't recreate because terraform is usually idempotent.
- Start a creation:
go run ./cmd/starter/main.go -action=create- Note that after the steps finish the workfow is still running
- Update:
go run ./cmd/starter/main.go -action=update -environment=env-id- if done with no change to the
main.tfit will be a no op. - if ami is changed in the
main.tfterraform will cause old instance to shut down and a new one to be created.
- if done with no change to the
- To approve
go run ./cmd/starter/main.go -action=approve -environment=env-id - To destroy
go run ./cmd/starter/main.go -action=destroy -environment=env-id- this will end the workflow (after deprovisioning)
The implementation of the get_forecast tool includes a 10 second sleep between the two HTTP requests. Experiment with the following:
- Run it with no firewall rules
- Add the firewall rules and enable the firewall
- Disable the firewall, accept the MCP tool execution and then enable the firewall within 10 seconds. Disable the firewall on the 11th second and see what happens.
We will simulate a network outage by adding firewall rules using pfctl. This repository includes a pf.rules file that will allow you to block API access to Cloudflare. The Cloudflare DNS resolution is fairly stable (some ther APIs like the National Weather Service are not) so the pf.rules file currently just has the domain name. You can check what these are right now with the following command:
dig +short api.cloudflare.com
The following commands are used to set and delete the rules, and enable and disable the firewall.
To set rules
sudo pfctl -f pf.rules
To remove the rules. WARNING: this will delete all rules - if you are using pfctl for real, use with caution.
sudo pfctl -F all
To see the current list of rules:
sudo pfctl -s rules
To enable the firewall
sudo pfctl -e
To disable the firewall
sudo pfctl -d