Practical lab guide
MicroBank 3: provision local cloud dependencies
Use Terraform to own the SNS-to-SQS routes and an S3 practice bucket in Floci.
Terraform
AWS
Predict before you try
Can Accounts acceptance alone prove this saved deposit reached Ledger exactly once?
Try it: Give infrastructure one owner →On this page
Give infrastructure one owner
Start the infrastructure profile from step 2. Run Terraform on the Mac, where 127.0.0.1:24666 reaches Floci. No real AWS account is required for this lab. The hardcoded local endpoints and dummy credentials below are intentional; do not convert this file into a real-cloud deployment by removing those settings.
Save infra/study/terraform/main.tf:
terraform {
required_version = ">= 1.12.0, < 2.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "6.66.0"
}
}
}
provider "aws" {
region = "ca-central-1"
access_key = "test"
secret_key = "test"
skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
s3_use_path_style = true
endpoints {
sns = "http://127.0.0.1:24666"
sqs = "http://127.0.0.1:24666"
s3 = "http://127.0.0.1:24666"
sts = "http://127.0.0.1:24666"
iam = "http://127.0.0.1:24666"
}
}
locals {
routes = {
requested = {
topic = "microbank-transactions-requested"
queue = "ledger-transactions"
}
settled = {
topic = "microbank-transactions-settled"
queue = "accounts-settlements"
}
}
}
resource "aws_sns_topic" "events" {
for_each = local.routes
name = each.value.topic
}
resource "aws_sqs_queue" "events" {
for_each = local.routes
name = each.value.queue
visibility_timeout_seconds = 30
message_retention_seconds = 86400
}
resource "aws_sqs_queue_policy" "from_topic" {
for_each = local.routes
queue_url = aws_sqs_queue.events[each.key].id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "sns.amazonaws.com" }
Action = "sqs:SendMessage"
Resource = aws_sqs_queue.events[each.key].arn
Condition = {
ArnEquals = { "aws:SourceArn" = aws_sns_topic.events[each.key].arn }
}
}]
})
}
resource "aws_sns_topic_subscription" "queue" {
for_each = local.routes
topic_arn = aws_sns_topic.events[each.key].arn
protocol = "sqs"
endpoint = aws_sqs_queue.events[each.key].arn
raw_message_delivery = false
depends_on = [aws_sqs_queue_policy.from_topic]
}
resource "aws_s3_bucket" "receipts" {
bucket = "microbank-study-receipts"
}
output "runtime" {
value = {
requested_topic_arn = aws_sns_topic.events["requested"].arn
settled_topic_arn = aws_sns_topic.events["settled"].arn
ledger_queue_url = aws_sqs_queue.events["requested"].id
receipts_bucket = aws_s3_bucket.receipts.id
}
}
There are nine resources: two topics, two queues, two queue policies, two subscriptions, and one bucket. Preserve the SNS envelope: Ledger extracts its Message property, so raw_message_delivery = true would change the consumer contract. The settlements queue deliberately has no consumer yet; observing its backlog is useful application evidence. The S3 bucket practices storage provisioning and is not claimed to receive application receipts.
Read one event route completely
Follow one key through the resource blocks. It creates a topic and queue, grants the intended topic permission to send to that queue, and subscribes the queue to the topic. References connect the generated identifiers; the explicit policy dependency handles the permission being present before subscription creation.
The two event routes account for eight resources; the independent practice bucket is the ninth. Terraform owns those API objects, while Compose owns the Floci process they live inside. Destroying the process first would remove the endpoint Terraform needs to perform its normal cleanup.
The runtime output is the handoff to the next lesson. terraform output -json runtime writes actual applied values to infra/study/runtime.json. Ansible reads that file and checks its expected local shape before rendering settings. Do not fill it with invented ARNs to bypass a failed apply.
Review, apply, and pass outputs forward
Use the specified provider version as the lab baseline, then commit the generated lock file. A provider schema check does not establish compatibility with every Floci version; record the pair you actually use.
terraform -chdir=infra/study/terraform init
terraform -chdir=infra/study/terraform fmt
terraform -chdir=infra/study/terraform validate
terraform -chdir=infra/study/terraform plan -out=local.tfplan
terraform -chdir=infra/study/terraform show local.tfplan
terraform -chdir=infra/study/terraform apply local.tfplan
terraform -chdir=infra/study/terraform output -json runtime > infra/study/runtime.json
chmod 600 infra/study/runtime.json
terraform -chdir=infra/study/terraform plan -detailed-exitcode
On a new empty emulator, review the nine planned creates. Stop and investigate unexpected existing resources or changes instead of deleting someone else's resources. After a successful apply, the last command should return 0 for no changes, 2 for a change proposal, or 1 for an error. Keep state private and retain it while the emulator resources exist.
The AWS provider authenticates with fixture credentials. Its skip flags are local-emulator settings, not cloud security practices. The queue policy records the intended topic-to-queue permission even if an emulator does not enforce every real AWS rule.
Checkpoint and sources
Explain every resource address, the dependency on the queue policy, and the runtime output contract in your notes. Step 4 consumes runtime.json without copying ARNs by hand. Sources: AWS provider custom endpoints↗, SNS subscriptions↗, SQS queue policies↗, Floci Terraform↗.
Prove what you learned
Inspect the result
Retain selected source/profile, case identity, expected balance and matching-entry count.
Explain it in your own words
Explain why your observation supports the stated outcome, and name one thing it does not prove.
Try a small variation
Repeat the same check after the documented restart or recovery; preserve the original case instead of creating a replacement.
Your evidence should show
- Record the context/revision or label the work as a paper or prepared fixture.
- Compare the prediction with the actual result from “Give infrastructure one owner”.
- Retain selected source/profile, case identity, expected balance and matching-entry count.
- Explain the transfer challenge and retain the same verification question.
Hint
Follow the case identifier across the outbox, message route and Ledger result.
Answer guide: compare after predicting
No. Repeat the idempotency key, then verify exactly one matching Ledger entry and the expected balance.
Compare this with your actual observation. A paper answer is useful reasoning practice; record execution separately.
Recall private networking, persistent volumes and bounded Python checks. Revisit the previous lesson.
Save the prediction, result and explanation ↓Your notes and evidence
Record observations, questions, or links to your work. Keep credentials out of your notes.
Back up or restore this path
Progress and notes stay in this browser. A backup contains only this learning path.