Module 3: Infrastructure As Code For An Ai Endpoint
7. Hands-on: verifying what can actually be applied
Description
bedrock.tf has two pieces: a Bedrock guardrail and an IAM role. Lesson 6 confirmed, with an official citation, that the first can't actually be applied on any free LocalStack plan. This lesson traces the exact boundary with the second — and it's more precise than "IAM is Hobby, so this does apply" suggests at first glance. There are two layers of honesty to separate here, not one, and this lesson separates them with the same care as the rest of the guide.
Connection to the module
This lesson closes the argument lesson 1 opened and lesson 6 confirmed from the other side: within the same bedrock.tf, two resources can have completely different fates when it comes to apply, and each one's reason gets explained separately, with its own evidence — never a generic "this is LocalStack, so everything behaves the same" rule.
This boundary's two layers, separated from the start
TWO DIFFERENT QUESTIONS, NOT ONE
Question 1: is the SERVICE (IAM, Bedrock) in the Hobby plan?
│
├── IAM: YES, confirmed since aws-core-services-guide.
└── Bedrock: NO, "Included in Plans: Ultimate" (lesson 6).
Question 2: does THIS WRITING SANDBOX have a LOCALSTACK_AUTH_TOKEN
exported, for the container to even start at all?
│
└── NO, for any service -- confirmed with the real exit code 55
from Module 1, lesson 7, reproduced again in this
module's lesson 6.
Question 1 is about the service: it distinguishes what's possible in principle, with any real Hobby account, on any machine. Question 2 is about this specific writing environment: even for a service that is in Hobby, here, today, the container doesn't start. Confusing these two questions — treating them as if they were the same — is exactly the mistake this lesson exists to prevent.
Step 1 — The answer to Question 1: IAM yes, Bedrock no, in principle
Lesson 6 already established this, with evidence: Bedrock is officially documented as "Included in Plans: Ultimate" — no Hobby token, no matter where it comes from, resolves it. IAM never had that restriction; it's one of the core services aws-core-services-guide already confirmed, with real execution, as part of the free plan since this ecosystem's first guide.
What this means in practice, for someone with their own machine and their own free, registered Hobby token:
docker run -d -p 4566:4566 -e LOCALSTACK_AUTH_TOKEN=$LOCALSTACK_AUTH_TOKEN localstack/localstack:latest
tflocal apply -target=module.bedrock_manifest_extractor_role -auto-approve
What to expect (representative, but precisely built from exactly the JSON terraform plan already produced in this module's lessons 4 and 5 — never an invented format):
module.bedrock_manifest_extractor_role.aws_iam_role.this: Creating...
module.bedrock_manifest_extractor_role.aws_iam_role.this: Creation complete after 1s [id=BedrockManifestExtractorRole]
module.bedrock_manifest_extractor_role.aws_iam_role_policy.this: Creating...
module.bedrock_manifest_extractor_role.aws_iam_role_policy.this: Creation complete after 0s [id=BedrockManifestExtractorRole:BedrockManifestExtractorRole-policy]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
bedrock_manifest_extractor_role_arn = "arn:aws:iam::000000000000:role/BedrockManifestExtractorRole"
awslocal iam get-role-policy --role-name BedrockManifestExtractorRole --policy-name BedrockManifestExtractorRole-policy
What to expect (representative, the exact same JSON lesson 4 already showed, now as the result of a read against a genuinely applied role):
{
"RoleName": "BedrockManifestExtractorRole",
"PolicyName": "BedrockManifestExtractorRole-policy",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "InvokeManifestExtractionModelOnly",
"Effect": "Allow",
"Action": "bedrock:InvokeModel",
"Resource": "arn:aws:bedrock:us-east-1::foundation-model/amazon.nova-lite-v1:0"
}
]
}
}
This would be a completely real read — IAM is a Hobby service, with no license restriction above that — if this command ran against a LocalStack actually started. The account ARN 000000000000 is the same one LocalStack assigns, without exception, to any emulated account — the same one you already saw in aws-core-services-guide since this ecosystem's first guide.
Step 2 — The answer to Question 2: why, even so, none of this ran in this sandbox
This is where the boundary gets fine. Step 1's complete flow — real apply, real awslocal, IAM working end to end — is exactly what would happen on your machine, not this writing sandbox's. The reason, already confirmed by direct execution in Module 1, lesson 7 and reproduced in this module's lesson 6, is specific to this environment: there's no LOCALSTACK_AUTH_TOKEN exported here, so the LocalStack container shuts down with exit code 55 before exposing a single service — IAM included, even though IAM itself has no plan restriction.
THE COMPLETE BOUNDARY, BOTH LAYERS TOGETHER
bedrock.tf
│
┌───────────────┴───────────────┐
│ │
module.manifest_extractor_guardrail module.bedrock_manifest_extractor_role
(aws_bedrock_guardrail) (aws_iam_role + aws_iam_role_policy)
│ │
▼ ▼
Service in Hobby? NO Service in Hobby? YES
("Ultimate" -- lesson 6) (confirmed since aws-core-services-guide)
│ │
▼ ▼
NEVER applies in Hobby, COULD apply in Hobby --
with or without a token, ON A MACHINE WITH A REAL TOKEN.
on any machine. In THIS sandbox: also doesn't run,
because of exit code 55 (Step 1
of lesson 6) -- an ENVIRONMENT
limit, not a service limit.
│ │
▼ ▼
REPRESENTATIVE REPRESENTATIVE in this
(service limit, specific sandbox
permanent, any (environment limit,
machine) resolvable with a token)
The practical outcome — both stay representative in this guide — is the same, but the reason behind each one is completely different, and that difference matters for anyone using this same pattern on their own machine: whoever exports their own real Hobby LOCALSTACK_AUTH_TOKEN is going to see the IAM role actually get applied, with no change to bedrock.tf — but will still be unable to apply the Bedrock guardrail, no matter what token they use, unless that token is from a paid Ultimate plan.
Step 3 — Confirming the boundary with the commands that did run: validate and plan, for both resources, no distinction
The only thing that does not vary between the guardrail and the role is exactly what this module's lessons 3, 4, and 5 already confirmed, with real execution: terraform validate and terraform plan run for real for both, with no difference in treatment. Confirm it once more, in a single command, on both:
terraform plan -input=false -no-color \
-target=module.manifest_extractor_guardrail \
-target=module.bedrock_manifest_extractor_role
What to expect (literal — already executed in this module's lesson 5, reconfirmed here):
Plan: 3 to add, 0 to change, 0 to destroy.
Three resources, with no distinction of "which one yes, which one no" — because, up to this exact point (plan, not apply), neither of the two needs anything real. This entire lesson's boundary is exclusive to apply onward — the dividing line this module's lesson 1 promised from the start.
Common mistakes
Concluding "this guide's IAM role was actually applied" because the lesson describes it in such detail (confusing representative precision with real execution mistake). What happens: someone, reading Step 1 in full — with Apply complete! and an awslocal producing JSON output —, assumes these commands ran in this sandbox. How to spot it: if your summary of this lesson includes the phrase "the role got applied in this environment." How to fix it: Step 1 is explicitly marked as representative — it precisely describes what would happen on a machine with a real token, built from the exact JSON terraform plan already produced (never invented), but it didn't run in this writing sandbox. The exact reason — exit code 55, no token — is in Step 2, immediately after.
Thinking that, because the IAM role "could apply" in principle, that makes it more real or more trustworthy than the Bedrock guardrail (ranking representative things mistake). What happens: someone treats this lesson's distinction as if one kind of "representative" were better than another. How to spot it: if your conclusion is "the IAM role is almost real, the guardrail is totally representative" as if there were different degrees of trust within the label itself. How to fix it: within this guide, both are equally representative — neither ran for real here — the only thing that changes is why, and that reason does matter for anyone replicating this guide on their own machine (the role resolves with a free token; the guardrail never, without a paid plan). The "representative" label itself has no degrees within this guide — it either ran, or it didn't.
Assuming that, because IAM is confirmed as Hobby, all future IAM resources in this guide (or in any guide in the ecosystem) can be applied in any sandbox, with no need to re-verify the token (generalizing about the environment, not just the service, mistake). What happens: someone, in a future lesson in this guide or another, automatically assumes "it's IAM, so it applies fine here." How to spot it: if your default expectation, in any new lesson in this ecosystem, is that a real apply runs without first checking whether the specific sandbox has a token exported. How to fix it: this lesson's Question 2 — does THIS environment have a token? — is independent of Question 1 — is the service in Hobby? — and needs to be confirmed every time, not assumed once for the whole ecosystem. This writing sandbox, specifically, has no token — that doesn't change between lessons or between guides.
Exercises
Exercise 1 — Classify the following three commands as "ran for real in this module" or "representative," and justify each with the exact reason. (a) terraform validate on the complete bedrock.tf. (b) tflocal apply on module.bedrock_manifest_extractor_role. (c) tflocal apply on module.manifest_extractor_guardrail.
See solution
(a) Ran for real — confirmed in this module's lessons 4 and 5, with no dependency on LocalStack or any token, by the mechanism lesson 1 explained. (b) Representative, due to the environment's limit — the service (IAM) is indeed in Hobby, but this specific writing sandbox has no LOCALSTACK_AUTH_TOKEN exported, so not even a Hobby service can be applied here; on a machine with a real token, this command would run for real, with no HCL changes. (c) Representative, due to the service's limit — Bedrock is documented as "Included in Plans: Ultimate" (lesson 6); no Hobby token, on any machine, would resolve this specific limit — only a paid Ultimate plan would.
Exercise 2 — Explain why this lesson needed to separate "is the service in Hobby?" from "does this sandbox have a token?" into two different questions, instead of a single "does this apply here?" question. What would be lost if the guide only answered the combined question?
See solution
The most useful information for anyone replicating this guide on their own machine would be lost: if the answer were only "no, it doesn't apply here," with no distinction of the reason, a reader wouldn't know whether their own attempt — on their own machine, maybe with a real token — would have a different result. Separating the two questions makes clear that the IAM role is a temporary and solvable limit (get a token, and it runs for real), while the Bedrock guardrail is a structural limit (no Hobby token resolves it, no matter whose it is or when it's obtained). Merging both questions into a single binary answer would have hidden exactly that difference, the more useful of the two.
Exercise 3 — Predict what would happen if someone, on their own machine, exported a real Hobby LOCALSTACK_AUTH_TOKEN and ran tflocal apply with no -target, on the complete andes-cargo-infra/ (lesson 5's seventeen resources). Would all seventeen get applied, or would apply fail at some point?
See solution
The fourteen inherited resources (DynamoDB, Lambda, S3, IAM from previous guides) and the BedrockManifestExtractorRole role (two resources, also IAM) would get applied for real, no problem — sixteen total. The only one that would fail is module.manifest_extractor_guardrail.aws_bedrock_guardrail.this — the Bedrock guardrail —, with the same license error lesson 6 documented, stopping apply at that specific point. Terraform wouldn't automatically roll back what already applied successfully before reaching that resource; the state would end up with sixteen resources actually created and one pending, exactly the kind of partial result that makes having a reviewed plan before any apply valuable in a real CI/CD flow — the same pattern cicd-and-gitops-on-aws-guide already built.
Summary and next step
This lesson precisely separated two questions a superficial glance might merge into one: whether a service is in LocalStack's Hobby plan, and whether this specific writing sandbox has a token that even lets the container start. IAM answers yes to the first and no to the second — an environment limit, solvable with a real token; Bedrock answers no to the first, a structural limit no Hobby token resolves. Both stay representative in this guide, for completely different reasons, each documented with its own evidence — never a single generic warning covering both cases.
Before moving on you should be able to: explain the difference between a "service" limit and a "writing environment" limit; predict, unaided, what would happen to someone with a real Hobby token trying to apply each of bedrock.tf's two resources; and confirm, from memory, that validate and plan never had this distinction — they ran the same for both, without exception, throughout this entire module.
Lesson 8 — the project that closes this module — brings the complete bedrock.tf and modules/bedrock-guardrail/ together as a single portfolio piece, with the honest ledger of what ran for real and what stayed representative, and why, all in one place.
Resources
- This guide's Module 1, lesson 7 (
07-hands-on-the-honest-attempt-against-bedrock-on-localstack.md) — the source of the realexit code 55, the foundation for this lesson's Question 2. - This module, lesson 6 (
06-the-exact-limit-why-apply-does-not-run-against-localstack-hobby.md) — the source of LocalStack's official citation about Bedrock, the foundation for Question 1 for that resource. aws-core-services-guide, Module 1 — the original source confirming IAM as a Hobby service.- LocalStack Docs — IAM — IAM's official coverage page, for a direct comparison against the Bedrock badge cited in lesson 6.