Repository navigation
Milestone 2: Real Terraform workflow - #4
Merged
Merged
Conversation
Shells out to a real terraform binary for fmt/init/validate/plan only --
apply/destroy stay on SimulatedTerraformTool until Milestone 3. Adds
tools/_subprocess_safety.py for explicit timeouts and secret redaction on
every real subprocess call, since state/auth output can contain secrets.
Plan parsing extracts only {address, action} from terraform show -json,
never raw attribute values, feeding the existing risk classifier
unchanged. Also renders LearningMoment.related_artifact, which had no
real caller before this milestone.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
workflows/terraform_flow.py runs fmt -> init -> validate -> plan -> risk analysis, teaching provider/state concepts just-in-time (gated by learner profile, matching local_flow's precedent) and diagnosing an auth failure on plan as an expected, honest outcome rather than a bug. cli/main.py wires RealTerraformTool in only for this command, keeping every other command's terraform tool simulated. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
projects/api_platform/infra/terraform/: resource group, container registry, Log Analytics workspace, tagged for easy identification and cleanup. Split into providers/variables/main/outputs.tf as a readability convention (Terraform still merges the whole directory). Fixes an ACR naming bug (hyphens aren't valid in that resource's name, unlike most others) caught by live terraform validate during review. .gitignore now excludes .terraform/ and *.tfstate* while keeping .terraform.lock.hcl committed. ProjectAnalyzer correctly now detects this real IaC. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
fmt -check, init -backend=false, and validate against projects/api_platform/infra/terraform/ -- no Azure credentials needed, no plan/apply in CI. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
docs/terraform-state.md is the reference the in-flow teaching moments link back to: desired vs. actual vs. Terraform state, provider, resource address, dependencies, drift, local vs. remote state and locking, state sensitivity, why deleting state isn't deleting infrastructure, why hand-editing state is dangerous. Updates README/roadmap/safety/ development docs to describe exactly what's real this milestone (fmt/ init/validate/plan, no apply, no real Azure execution) and documents the RUN_AZURE_INTEGRATION_TESTS convention Milestone 3 will need. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CLAUDE.md: durable rules on product direction, the Tool/ToolService gateway, real-vs-simulated labeling, and quality gates. .claude/agents/: terraform-reviewer, security-reviewer, learning-reviewer -- read-only review personas used during this milestone's own development. .claude/hooks/confirm_destructive_command.py: a PreToolUse Bash hook that forces explicit confirmation before terraform apply/destroy, az group/ resource delete, git push --force, and similar commands run directly via Bash, as a backstop for when a command bypasses ToolService entirely. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
Adds
RealTerraformTool(realfmt/init/validate/planvia subprocess) alongside the existingSimulatedTerraformTool, a newdevops-learn terraformcommand with its own vertical-slice workflow, real committed Terraform config for the bundledprojects/api_platformsample, Terraform CI checks, three project review subagents, a destructive-command Bash hook, and a projectCLAUDE.md.Why
docs/roadmap.mdalready scoped this as Milestone 2 (real Terraform depth before Milestone 3's Azure vertical slice). No Azure credentials are available on the dev machine this was built on, so the milestone deliberately stops at a trustworthyplan— everything that doesn't require Azure access is real and tested;planrequires credentials and fails cleanly, with an explanation, without them.Real Terraform capabilities
terraform fmt -check,init -input=false,validate -json, andplan(viaplan -out=+show -json) all run for real viaRealTerraformTool(src/devops_learn/tools/terraform_tool.py).terraform show -jsonis parsed into{create, change, replace, destroy}and fed unchanged into the existingvalidation/terraform_plan_analysis.analyze()risk classifier.devops-learn terraformusesRealTerraformTool;analyze --real-tools,local, andreviewkeepSimulatedTerraformToolfor theterraformtool name.What remains simulated
apply_approved_plan/destroy_approved_environmentare not declared onRealTerraformToolat all — invoking either raisesKeyErrorrather than silently falling back to simulated behavior. They stay onSimulatedTerraformTooluntil Milestone 3.Safety / approval design
RealTerraformTooloperation isRiskLevel.SAFE,requires_approval=False— no new approval-gated path was introduced this milestone.tools/_subprocess_safety.pyadds an explicit timeout and secret redaction (KEY=value-shaped) to every real subprocess call, plus output truncation.planparsing only ever extracts{address, action}fromterraform show -json— never rawbefore/afterattribute values — as the primary defense against leaking sensitive plan data; free-text redaction is defense in depth on top of that..claude/hooks/confirm_destructive_command.py(PreToolUseonBash) forces an explicit "ask" confirmation beforeterraform apply/destroy,az group/resource delete,git push --force, etc., as a backstop against those commands running directly viaBashoutsideToolService..claude/agents/{terraform,security,learning}-reviewer.md) were run against this diff during development; their findings (an invalid ACR resource name, a hook crash on malformed payloads, missing learner-profile gating on teaching moments, a deadrelated_artifactfield, and more) are all fixed in this branch.Credential model
No credentials are used or required for
fmt/init/validate.planneedsaz loginorARM_CLIENT_ID/ARM_CLIENT_SECRET/ARM_TENANT_ID/ARM_SUBSCRIPTION_ID; without them it fails cleanly andworkflows/terraform_flow.pyexplains why rather than treating it as a bug. Nothing in this PR reads or writes real credentials.Cost implications
None. No
applypath exists yet, so no Azure resource can be created by this code.Terraform/Azure learning objectives
devops-learn terraformteaches Terraform state, provider, and resource-address concepts just-in-time (gated by learner profile, matchinglocal_flow.py's precedent — a learner already strong in Terraform sees no teaching moments), withdocs/terraform-state.mdas the fuller reference. A credential-lessplanfailure is turned into an explicit diagnosis of Azure authentication rather than a hidden error.Test results
pytest: 116 passedflake8 src tests projects/api_platform/app projects/api_platform/tests: cleanmypy(platform, strict): clean, 102 filesmypy app --strict(demo app): clean, 4 filesterraform fmt -check -recursive/terraform init -backend=false/terraform validateagainstprojects/api_platform/infra/terraform/: passManual verification
Ran
devops-learn terraformend to end on the dev machine: realfmt/init/validatesucceed;planfails cleanly with a realaz loginerror (no Azure credentials on this machine), correctly diagnosed by the workflow. Verified the learner-profile gating skips both teaching moments whenterraform=strongis set. Verified therelated_artifactcross-link now renders. Verified the destructive-command hook against sample stdin JSON for all nine target patterns plus edge cases (case-insensitivity,-fshort flag, malformed payload shapes).Known limitations
projects/api_platformsample; there's no on-the-fly config generation for arbitrary projects yet."az login"/"authoriz"/"authent") — it fails safe (falls through to a generic message) but won't catch every possible azurerm auth error phrasing.terraform show -jsonoutput is redacted/truncated (20k char cap) before JSON parsing; a plan large enough to exceed that cap would fail to parse. Not a concern at this project's current 3-resource scale.Next milestone
Milestone 3: first Azure vertical slice (resource group + ACR provisioned for real,
applybound to an approved plan, independent Azure verification viaaz, a real deploy of the bundled FastAPI app, one troubleshooting exercise, and a safe drift exercise) — perdocs/roadmap.md, gated on the user setting up real Azure credentials.🤖 Generated with Claude Code