STACKTOIT · READING A REAL ASSESSMENT
How to read a real StackToIT migration assessment
An assessment folder holds well over a hundred files. You do not read them in order; you read five things: the summary, the class of each unit, one unit in full, one decision and the plan step it holds. This guide walks that path on the published AWS Retail Store run, so every figure below can be checked against the public example.
Start with the summary, then what is not established
The published AWS Retail Store run: 282 Terraform resources, 44 migration units. Left: the scan block of console.txt. Right: the validation matrix the run prints below it.
StackToIT Assess
────────────────────────────────────────────
Source: terraform/ecs/default
Source files: 105
Resources scanned: 282 (0 in the root module · 282 inside 23 module calls)
support resources: 34 (+12 Terraform helpers, out of scope; 34 + 12 = the 46 support nodes of summary.support.nodes in assessment.json)
Migration units: 44 known-present (44 instances) · 8 conditional (+41 switched off — see Not in a wave)
Unit dependencies: 109
Modules: 23 module calls (13 external, 10 local): 23 loaded, 0 not downloaded, 0 invalid
module.dependencies ../../lib/dependencies 11 units (RECONFIGURE 7 · REPLATFORM 3 · REDESIGN 1) · 13 switched off · 77 resources (+10 nested calls)
module.retail_app_ecs ../../lib/ecs 30 known-present units (RECONFIGURE 24 · REPLATFORM 6) · 3 conditional · 3 switched off · 126 resources (+8 nested calls)
module.tags ../../lib/tags 0 units · 0 resources
module.vpc ../../lib/vpc 3 known-present units (RECONFIGURE 3) · 5 conditional · 25 switched off · 79 resources (+1 nested call)
Source checks: Built-in source checks passed — OpenTofu validation of the source did not run (LOCK_FILE)
Source completeness: RUNTIME_TOPOLOGY_PARTIAL — 8 unit(s) have a count/for_each that is not known statically; whether and how often they exist is decided at plan time
STACKIT mapping
────────────────────────────────────────────
DIRECT 0
RECONFIGURE 34 ███████████████████████ 77%
REPLATFORM 9 ██████ 20%
CONTAINERIZE 0
REDESIGN 1 █ 2%
MANUAL_REVIEW 0
BLOCKED 0 (the mapping class BLOCKED, not the execution blockers, which Migration readiness counts)
Confidence HIGH 0 · MEDIUM 35 · LOW 9Validation matrix (what has been established; nothing here approves the draft)
Source completeness: PARTIAL (source status RUNTIME_TOPOLOGY_PARTIAL)
Draft generation: GENERATED (the draft is in terraform/)
StackToIT draft checks: PASSED (100 checks, none found a violation)
Global invariants: PASSED (12 of 12 invariants PASS)
OpenTofu validation: PASSED (tofu init -backend=false and tofu validate accepted the draft; tofu 1.13.1)
Output schema validation: PASSED (verified at publish: every JSON file that has a schema is validated against it (see run_verification in manifest.json))
STACKIT plan: NOT RUN (this build runs no plan against a STACKIT project)
Architect approval: NOT PROVIDED (no review decision is recorded for this run)
Semantic equivalence: NOT ASSESSED (this build has no measure for it)
Safe to apply: NOT ASSERTED (StackToIT never asserts it)Counts first
The source has 282 resources and 44 units, 8 more units are decided only when OpenTofu plans, and the source status is RUNTIME_TOPOLOGY_PARTIAL. Read that before the table: it says the inventory is partial, and why.
A class is the work, not a verdict
RECONFIGURE 34, REPLATFORM 9, REDESIGN 1, none BLOCKED. Confidence is MEDIUM for 35 units and LOW for 9, with none HIGH. The architect report gives the reason: the example ships no lock file, and until the provider version is pinned (commit .terraform.lock.hcl) its units are capped at MEDIUM.
The matrix says what the run did not do
The STACKIT plan was not run, no architect approval is recorded, equivalence is not assessed and safe-to-apply is never asserted. A passing static validation of the draft is not approval, and the run labels it that way.
One unit in full: the DynamoDB table
The most instructive unit of the run is the one classed REDESIGN. Left: its record in assessment.json (id, class, confidence, target, reason and review). Right: the stackit_mongodbflex_instance block the run drafted for it, review comments left out.
{
"id": "module.dependencies.module.dynamodb_carts.aws_dynamodb_table.this",
"result": {
"class": "REDESIGN",
"confidence": "MEDIUM",
"target": {
"service": "STACKIT MongoDB Flex (alternative — data-model redesign; PostgreSQL Flex JSONB is the other option)",
"resources": [
"stackit_mongodbflex_instance"
]
},
"reason": "There is no DynamoDB-compatible service on STACKIT. The access layer is rewritten against a document store (MongoDB Flex) or relational JSONB (PostgreSQL Flex); key design, capacity modes and conditional writes do not carry over. The draft is an alternative, not an equivalent: one MongoDB Flex instance for the estate's tables, each table a collection with its keys and secondary indexes named as MongoDB indexes.",
"review": [
"Rewrite the data access layer against the MongoDB API (queries, conditional writes, transactions).",
"Size the instance from the tables' data volume and request load; capacity units do not translate."
]
}
}resource "stackit_mongodbflex_instance" "dynamodb_tables" {
project_id = var.project_id
name = "dynamodb-tables"
acl = var.dynamodb_tables_allowed_cidrs
version = var.dynamodb_tables_version
flavor = {
cpu = var.dynamodb_tables_cpu
ram = var.dynamodb_tables_ram
}
replicas = 3
storage = {
class = var.mongodbflex_storage_class
size = var.dynamodb_tables_storage_gb
}
backup_schedule = "0 2 * * *"
options = {
type = "Replica"
point_in_time_window_hours = var.dynamodb_tables_pitr_hours
}
}The reason is the contract
There is no DynamoDB-compatible service on STACKIT, so the draft is an alternative, not an equivalent. The reason names what does not carry over: key design, capacity modes and conditional writes.
Review items are your to-do list
The two review lines are tasks: rewrite the data access layer, and size the instance, because capacity units do not translate.
Variables mark what only you know
cpu, ram, storage and the access list are variables without a value. The run does not guess the size of your data.
One decision: from the register to the plan step
A question the code cannot answer becomes a record in decisions.json, a stub in review/decisions.yaml and a manual step in execution-plan.json. This is the NAT egress address, from the same run.
{
"id": "DEC-NET-STACKITROUTE-NAT-FIXED-EGRESS-ADDRESS-LOST",
"kind": "NAT_FIXED_EGRESS_ADDRESS_LOST",
"status": "OPEN",
"owner_role": "Network Architect",
"required_by_phase": "BEFORE_PILOT",
"options": [
{
"id": "accept-the-platform-egress-address",
"execution_effect": "ACK_CURRENT_ARTIFACT"
},
{
"id": "reserve-a-public-ip-per-server",
"execution_effect": "REGENERATE_REQUIRED"
}
]
} # DEC-NET-STACKITROUTE-NAT-FIXED-EGRESS-ADDRESS-LOST
# NAT_FIXED_EGRESS_ADDRESS_LOST (PLAN_DECISION, status OPEN, 1 member)
# affects: module.vpc.module.vpc.aws_route_table.private
# options (name one in chosen_option):
# accept-the-platform-egress-address: the draft already carries it out: a review frees the steps that wait for it
# reserve-a-public-ip-per-server: changes the draft, and no answer of this build writes it: change the source (or the draft by hand) and run again; a review frees nothing
- id: DEC-NET-STACKITROUTE-NAT-FIXED-EGRESS-ADDRESS-LOST
resolution: ""
# An option above that changes the draft frees no step by a review alone: its line says how the draft comes to carry it. A review that names none of the options frees nothing either.
# chosen_option: <one of the options above>
rationale: ""
evidence_refs: []
reviewed_by: Network Architect
reviewed_at: "<YYYY-MM-DD>"
members_sha256: sha256:cbfcbf1e12e98cb3608485caffc5de82e33e3517a0845fc9ca14cbcf7e207d0e
artifact_sha256: sha256:37a4992d7cb1b57ebe73b1567f6fec7b6f2f3de527370a9ba50c9d2e600bccea{
"id": "DEC/routing/stackitroute:module.vpc.module.vpc.aws_route_table.private:NAT_FIXED_EGRESS_ADDRESS_LOST",
"mode": "MANUAL",
"phase": "PREPARE",
"capability": "decision",
"operation": {
"manual": {
"owner_role": "Network Architect",
"when": "BEFORE_PILOT",
"success_condition": "the decision DEC-NET-STACKITROUTE-NAT-FIXED-EGRESS-ADDRESS-LOST is answered (accepted or rejected, with its evidence) in the inputs of the run and is no longer open when the run is repeated"
}
},
"runs_as_written": false
}Three files, one id
The decision id DEC-NET-STACKITROUTE-NAT-FIXED-EGRESS-ADDRESS-LOST appears in all three, so an answer can be traced from the register to the review and to the step that waits for it.
Options say what they change
accept-the-platform-egress-address is carried out by the draft as written, so a review frees the steps that wait for it. reserve-a-public-ip-per-server changes the draft and needs a rerun; a review alone frees nothing.
Inputs and reviews are different
A design answer goes to the inputs file and regenerates the draft. A review records that an architect looked and decided. The next run reads both; a review alone rewrites no Terraform.
Where to look, in order
| Step | File | What you take from it |
|---|---|---|
| 1 · Summary | index.html, console.txt | Source completeness, class distribution, package state, first blocker |
| 2 · Units | assessment.json | Class, confidence, target, reason, review per unit, with the source line |
| 3 · Draft | terraform/ | The STACKIT Terraform, with NOT CARRIED comments per setting |
| 4 · Decisions | decisions.json, review/ | Open questions with options, owner role and phase |
| 5 · Plan | execution-plan.json, execution/ | Ordered steps, who does them, what each waits for |
| 6 · Data | migration/data-movement.md | Stateful units and the open data questions |
| 7 · Integrity | manifest.json | The files of the run and their checksums for stacktoit verify |
File names as the published run writes them.
Reading rules that save arguments
Zero can run as written
The Retail run says 48 of 57 automated steps are free of decisions, yet 0 can run as written because they wait for values such as project_id. Free of decisions is not ready to apply.
A default is not an answer
Two records rest on a draft default: public endpoint and size derived from the source. Until someone confirms or changes them, they are open.
Not carried means said
Every setting the draft leaves out is named in the file with the reason. The run reports 404 source properties checked on generated units, 204 carried or explained and 200 not carried, with none unaccounted.
Verify before you rely on a folder
stacktoit verify checks the files against the manifest. For the published run it reports 145 files matching, 7 of 7 checks passing.
Nothing is applied
The run reads code and writes files. It creates nothing in any cloud.
Questions reviewers ask
Why are there no HIGH confidence units in the run?
The source provider is unpinned: the Retail example ships no lock file. Confidence is capped at MEDIUM until the exact provider version is pinned.
Does a REPLATFORM class mean the move is easy?
It means the workload moves to a different platform model. Read the review items and the NOT CARRIED comments to size the effort.
Where do I put my answers?
Copy inputs-template.yaml and decisions-template.yaml outside the run folder, fill the requested fields without changing their identifiers, and pass them with --inputs and --decisions on the next run.
Can I trust the Terraform as it is?
It is a draft for review. The run validated it statically, and no plan was run against a STACKIT project.
Sources
Your next move: STACKIT.
Tell us about your estate. We show you what StackToIT delivers for it: target architecture, Terraform artifacts and a structured migration plan.