HUAWEI CLOUD → STACKIT · TUTORIAL
Huawei Cloud Terraform to STACKIT, step by step
You have a Huawei Cloud estate in Terraform and want to know what a move to STACKIT asks of you. This walk-through takes one small module from the command to the reviewed result: what to run, which files to open first, how the one open request is answered and what the second run changes. Every value shown was written by the run on 4 November 2026.
The input: a module that uses variables
Six resources in one main.tf (a VPC, a subnet, a security group with one rule, a server and an OBS bucket), a variables file and a terraform.tfvars file. Left: the variables. Right: the server and the bucket, which read them.
variable "project" {
type = string
default = "shop"
}
variable "image_id" {
type = string
}
variable "flavor_id" {
type = string
default = "s6.large.2"
}resource "huaweicloud_compute_instance" "app" {
name = "${var.project}-app-01"
image_id = var.image_id
flavor_id = var.flavor_id
availability_zone = "eu-west-101a"
security_group_ids = [huaweicloud_networking_secgroup.app.id]
system_disk_type = "SSD"
system_disk_size = 40
network {
uuid = huaweicloud_vpc_subnet.app.id
}
}
resource "huaweicloud_obs_bucket" "uploads" {
bucket = "${var.project}-uploads"
acl = "private"
versioning = true
}Variables are resolved
terraform.tfvars supplies image_id, so the run reports the source as complete. A variable without a default and without a value would be named as a gap instead; the DMS guide shows that case.
Keep the lock file
The run flags every unit with a warning that the provider version is not pinned by a lock file. Declaring the version in required_providers is not enough for it; commit .terraform.lock.hcl next to the module.
Nothing leaves your machine
StackToIT reads the files. It does not connect to a Huawei Cloud account, and the run was made offline.
The command and the first summary
One command with the module and an output folder, in the syntax of the StackToIT CLI documentation; the two folder names are examples, the demo itself was started from inside the module folder with a separate output folder. The run used --offline and --no-validate: the second skips the OpenTofu check of the source, which needs the provider available locally. The summary is the first thing to read.
stacktoit assess ./shop-terraform ./shop-out --offline --no-validateSource 5 migration units · visibility: Complete
Target 0 Terraform-only · 0 hybrid · 0 API-only · 5 manual · 0 no resource (of 5 units the draft considered)
Execution Partially executable · 0 Terraform · 0 API · 0 design inputs · 5 manual migration work · 0 lifecycle (cutover/verify/retire) (of 5 plan steps)
Decisions no open item · no decision holds a plan step · Inputs: 0 design inputs wait · 5 open requests (1 blocks the draft — inputs-template.yaml marks it, 4 are preferences this build cannot apply — inputs-template.yaml, NOT APPLICABLE IN THIS BUILD, says where to set each)
Checks 12/12 invariants passed (9 of them checked nothing) · Static validation: not run · Published (manifest.json lists every file of the package except itself and the review/ files you fill in)Five units, not six
The security group rule is assessed together with its group, so six resources make five units.
Nothing drafted
"0 Terraform-only … 5 manual" is the honest reading for a Huawei Cloud source on this build: every unit is classified and given a target and a reason, and a person sets up the STACKIT side.
Read the last two lines
One open request blocks the draft, and the run states that 4 more are preferences it cannot apply. Both are explained further down.
Which file answers which question
The run writes a folder, not a single report. This is the order in which these files were useful for this module.
| File | Question it answers | What this run held |
|---|---|---|
| index.html | What does the whole estate look like? | The unit-by-unit view; it opens offline in a browser. |
| assessment.json | Which class, target and reason does each resource get? | Five units, all RECONFIGURE: the security group with confidence LOW, the other four MEDIUM. |
| engineering/architect-report.md | Why, and what is left to us? | One entry per unit with the evidence behind its rule and the provider warning. |
| migration/technical-sequencing.md | In which order do the units move? | Three waves, derived from the references between the resources. |
| migration/data-movement.md | Which data has to move, and who decides how? | One stateful unit, the bucket; its mechanism is UNKNOWN and the state is DECISION REQUIRED. |
| execution/manual/actions.md | What does a person do? | Five manual steps in four work packages; the first is owned by the Network Architect. |
| review/inputs.yaml | What does the run still ask? | One stub, target.region, which blocks the draft. |
| terraform/ | What is the STACKIT draft? | terraform.tf, variables.tf and README.md; no resource blocks. |
File names and counts are from the run of this module. The full set of deliverables is listed in the StackToIT documentation.
The workflow in seven steps
The same sequence works for a large estate; only the reading takes longer.
Point the run at the root module
Choose the directory that declares the infrastructure, not a subdirectory of a larger configuration. Keep the lock file and the variable files next to it.
Run offline
--offline prohibits outbound requests, so the result depends only on your files. Add --var-file for values that do not live in the standard variable files.
Read the summary first
Check source visibility (here: Complete), the number of units, how many are drafted and how many are manual, and how many requests are open.
Open assessment.json
Each unit has a class, a confidence, a target service and a reason. Treat LOW confidence as a reason to read the report entry before planning.
Read the order
technical-sequencing.md lists the waves. Units in one wave can move in parallel; a unit moves only after the units it depends on.
Answer what is asked
Copy review/inputs.yaml out of the output folder, fill the value lines, keep your copy under version control and pass it back with --inputs.
Hand the work packages on
actions.md groups the manual steps by role. Give each owner their package and keep the run folder as the record of what was asked at the time.
Answering the open request
The stub is written by the run and bound to this source; you fill value and leave the other lines alone. Left: the stub as written. Right: the same lines in the copy passed to the second run.
- id: INP-TSE-1eaf5b60fa62
kind: TARGET_SELECTION
topic: target.region
scope:
profile: default
# options: eu01 | eu02
value: "" # pick one of the options above
# explanation: why you chose this (optional; remove the # to write it)
answered_by: CUSTOMER_ARCHITECT # a role, not a person
answered_at: "2026-11-04" # YYYY-MM-DD - id: INP-TSE-1eaf5b60fa62
kind: TARGET_SELECTION
topic: target.region
scope:
profile: default
# options: eu01 | eu02
value: eu01
# explanation: why you chose this (optional; remove the # to write it)
answered_by: CUSTOMER_ARCHITECT # a role, not a person
answered_at: "2026-11-04" # YYYY-MM-DDWhat the second run changed
The second run used --inputs with the answered copy and wrote a new output folder. Compare the Decisions line of the console: first run on the left, second run on the right.
Decisions no open item · no decision holds a plan step · Inputs: 0 design inputs wait · 5 open requests (1 blocks the draft — inputs-template.yaml marks it, 4 are preferences this build cannot apply — inputs-template.yaml, NOT APPLICABLE IN THIS BUILD, says where to set each)Decisions no open item · no decision holds a plan step · Inputs: 0 design inputs wait · 4 open requests (0 block the draft, 4 are preferences this build cannot apply — inputs-template.yaml, NOT APPLICABLE IN THIS BUILD, says where to set each)Changed
The blocking request is gone: 5 open requests became 4, and none of the remaining four blocks the draft. The second run also reports the answer as applied, with 0 stale and 0 invalid.
Not changed
The draft is the same: variables.tf already carried eu01 as the region default, and the units are still manual. The answer unblocks the draft; it does not write STACKIT resources for a Huawei Cloud source.
Four preferences
Public addresses, egress, observability and the target project are recorded as requests this build cannot apply. The template says where each is set instead, for example the project_id variable of the draft.
Keep both folders
A rerun on the same output folder keeps the earlier run in a previous-<run id> subfolder by default; separate folders, as here, make the comparison easier.
The order, as the run wrote it
From technical-sequencing.md. The subnet waits for the VPC, and the server waits for the subnet and the security group; the bucket depends on nothing and sits in wave 1.
## Wave 1 — 3 unit(s)
| Unit | Class | Target |
|---|---|---|
| `huaweicloud_networking_secgroup.app` | RECONFIGURE / LOW | STACKIT security groups |
| `huaweicloud_obs_bucket.uploads` | RECONFIGURE / MEDIUM | STACKIT Object Storage |
| `huaweicloud_vpc.main` | RECONFIGURE / MEDIUM | STACKIT network |
## Wave 2 — 1 unit(s)
| Unit | Class | Target |
|---|---|---|
| `huaweicloud_vpc_subnet.app` | RECONFIGURE / MEDIUM | STACKIT network |
## Wave 3 — 1 unit(s)
| Unit | Class | Target |
|---|---|---|
| `huaweicloud_compute_instance.app` | RECONFIGURE / MEDIUM | STACKIT Compute Engine |
## Cutover (decision support — performed by the customer)
No cutover facts: the scan and the draft established no DNS record set whose value the draft re-binds to a generated address, no public address re-binding, no DNS record unit, no certificate unit and no open cutover decision. That is not a statement that nothing has to be cut over: a name, an address or a certificate outside the scanned source is not seen, and a unit that exists only if OpenTofu decides so at plan time is not read here. StackToIT performs no cutover and gives no time estimate.How the five units came out
| Huawei Cloud resource | STACKIT target | Migration class |
|---|---|---|
| VPC | STACKIT network, with a Network Area where routing is needed | Reconfigure |
| Subnet | STACKIT network with its own address range | Reconfigure |
| Security group and its rule | STACKIT security groups | Reconfigure |
| ECS instance | STACKIT Compute Engine | Reconfigure |
| OBS bucket | STACKIT Object Storage | Reconfigure |
Classes as the run decided them. The security group is the one LOW unit. The run notes that its rule is catalogued without a mapping, so what the rule does to the group is not assessed; read its direction, port and source range yourself.
Decisions that stay with the architects
The run names these; it does not make them.
Region
eu01 or eu02. The answer above picked eu01; the other region is a different catalogue and a different answer.
Security group rules
STACKIT rules only allow traffic, so a deny rule or a priority order in the source needs a decision. This module has one ingress rule on port 443 open to 0.0.0.0/0; decide whether that stays.
Image and flavor
The flavor becomes a STACKIT flavor and the image is rebuilt or imported; neither is chosen for you.
How the bucket data moves
The size, the change rate and the tolerated downtime are not in the Terraform, so the run records the mechanism as undecided. The OBS guide covers the options.
Questions teams ask before the first run
Does the workflow need access to our Huawei Cloud account?
No. StackToIT reads Terraform and OpenTofu root modules that use the huaweicloud/huaweicloud provider and does not connect to a Huawei Cloud account.
Why does the run say 0 units drafted?
For Huawei Cloud sources this build classifies each resource and names a STACKIT target and a reason, and it writes terraform.tf, variables.tf and a README for the target, but no STACKIT resource blocks. The architects write those from the classification and the report.
What if a variable has no value?
The run names the gap and marks the source as incomplete rather than guessing. Supply the value with a variable file or --var-file and run again; the unit is then assessed with it.
Can we run it again after every change to the Terraform?
Yes. Each run reads the source as it is, writes a new run id and binds answers to that source; an answer written for something that changed is reported as stale and applies nothing.
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.