AZURERM + AZAPI → STACKIT · TUTORIAL
AzureRM and AzAPI Terraform to STACKIT: the workflow
StackToIT reads Terraform written with the azurerm or the azapi provider, offline and without an Azure account, and writes a STACKIT draft plus the list of decisions it cannot make for you. This tutorial runs one demo estate that mixes both providers, shows how each resource type is read and puts the review work in order.
One command, one summary
A demo estate written for this page and assessed by StackToIT on 4 November 2026 with providers azurerm 5.6.0 and azapi 2.12.0. Top: the command, with the output path shortened. Bottom: selected lines of the console summary, verbatim. The demo was run with the optional source validation switched off.
stacktoit assess . ./stacktoit-out --offline --no-validateSource 5 migration units · visibility: Complete
Target 4 Terraform-only · 0 hybrid · 0 API-only · 0 manual · 1 no resource (of 5 units the draft considered)
STACKIT draft (this folder/terraform): 4 units generated · 1 need no resource · 0 left for the architect · 7 variables
Critical open settings 11 (4 resolved of the 15 the units state)Seven resources, five units
The estate holds a resource group, a network with one subnet, a storage account, a Service Bus namespace and an Application Insights component written with azapi, and one azapi update. The run groups them into five units to assess.
Nothing connects to Azure
The StackToIT documentation states that it reads Terraform written with the azurerm or azapi provider and does not connect to an Azure account. The run needs the code, not credentials.
Open settings are the work
Eleven critical settings stay open in this run; four of the fifteen the units state are resolved. Five of the open ones are the resource-group scope, one per unit, for the Identity Architect; the rest are names, the namespace settings, the address space and data residency.
azapi resources in, STACKIT resources out
First pane: the three azapi resources from the input. Then the blocks the engine wrote for the first two, one pane per file; the review comments above each block in the file are left out here.
resource "azapi_resource" "queue_namespace" {
type = "Microsoft.ServiceBus/namespaces@2024-01-01"
name = "sb-portal-gwc"
location = azurerm_resource_group.app.location
parent_id = azurerm_resource_group.app.id
body = {
sku = {
name = "Standard"
tier = "Standard"
}
properties = {
minimumTlsVersion = "1.2"
}
}
}
resource "azapi_resource" "app_insights" {
type = "Microsoft.Insights/components@2020-02-02"
name = "appi-portal-gwc"
location = azurerm_resource_group.app.location
parent_id = azurerm_resource_group.app.id
body = {
kind = "web"
properties = {
Application_Type = "web"
RetentionInDays = 90
}
}
}
resource "azapi_update_resource" "storage_tls" {
type = "Microsoft.Storage/storageAccounts@2023-05-01"
resource_id = azurerm_storage_account.assets.id
body = {
properties = {
minimumTlsVersion = "TLS1_2"
}
}
}resource "stackit_rabbitmq_instance" "messaging" {
project_id = var.project_id
name = "messaging"
plan_name = var.rabbitmq_plan_name
version = var.rabbitmq_version
parameters = {
sgw_acl = join(",", var.rabbitmq_allowed_cidrs)
}
}resource "stackit_observability_instance" "migrated" {
project_id = var.project_id
name = "migrated-observability"
plan_name = var.observability_plan_name
}The type string decides
An azapi resource has a type such as Microsoft.ServiceBus/namespaces plus an API version, and a body. The run identifies the unit by the type and reads the body for settings, so the Service Bus namespace is assessed as a Service Bus namespace.
Shared instances
Both targets are shared instances: one RabbitMQ instance for all queues and topics, one Observability instance for telemetry. Queues and exchanges are yours to create, and the draft says the migrated alarms are rewritten as alert rules.
Values without a default
The RabbitMQ plan, its version and the allowed CIDRs are variables without defaults. For the version, the catalogue and STACKIT's lifecycle page disagree, and the draft says so rather than choose.
How the units were classified
Excerpt of assessment.json for four of the five units: the two azapi units, the azurerm storage account and the subnet. Only id and the class, confidence and target service of each result are shown; the values are as the run wrote them.
[
{
"id": "azapi_resource.queue_namespace",
"result": {
"class": "REPLATFORM",
"confidence": "MEDIUM",
"target": {
"service": "STACKIT RabbitMQ"
}
}
},
{
"id": "azapi_resource.app_insights",
"result": {
"class": "RECONFIGURE",
"confidence": "MEDIUM",
"target": {
"service": "STACKIT Observability (OpenTelemetry traces, metrics, logs)"
}
}
},
{
"id": "azurerm_storage_account.assets",
"result": {
"class": "REPLATFORM",
"confidence": "MEDIUM",
"target": {
"service": "STACKIT Object Storage"
}
}
},
{
"id": "azurerm_subnet.app",
"result": {
"class": "RECONFIGURE",
"confidence": "MEDIUM",
"target": {
"service": "STACKIT network"
}
}
}
]How each input type is read
| Source resource | STACKIT target | Class |
|---|---|---|
| azapi_resource, Microsoft.ServiceBus/namespaces | RabbitMQ instance | Replatform |
| azapi_resource, Microsoft.Insights/components | Observability instance | Reconfigure |
| azurerm_storage_account | Object Storage bucket | Replatform |
| azurerm_subnet | STACKIT network, same IPv4 prefix | Reconfigure |
| azurerm_virtual_network | No resource; Network Area only if networks must route privately | Reconfigure |
| azapi_update_resource | Support resource: no unit, critical settings listed for a person | None |
Classes as the run decided them, MEDIUM confidence for each unit. The update resource is listed under support resources in terraform/README.md.
The workflow in order
Pin the providers
Declare azurerm and azapi versions in required_providers and commit the lock file. Without a lock entry the run warns that the version is not proven and caps confidence at MEDIUM.
Assess
Run the command from the folder with the root module. Use the offline option when the machine has no network.
Read the first blocker
The console names the first blocker and the next action with an owner role. In this run both point to the Application Insights unit, whose open setting in decisions.json is the resource-group scope, owned by the Identity Architect. Separately, inputs-template.yaml marks the target region as the one request that blocks the draft: until it is answered, region-dependent requests are derived from the eu01 catalogue.
Answer in the inputs file
Copy inputs-template.yaml out of the run folder, fill the value lines such as the region, pass the file with --inputs and run again on the same output folder. The earlier run is replaced.
Fill the target values
Variables without defaults, such as the RabbitMQ plan and version, go into the copy of target-values-template.tfvars; run again and the run writes the values it takes into terraform/. Your own variables file works too.
Review the drafts, then plan
Read terraform/ with the platform team, then run tofu init, validate and plan in a test project. StackToIT never runs apply.
AzAPI specifics to expect
The body is dynamic
The azapi provider takes the request body as a dynamic value, so one resource type can carry any property of the Azure API. Settings arrive as nested keys, and the run lists them in dotted form, for example body.properties.minimumTlsVersion.
Updates are not owned resources
azapi_update_resource modifies properties of a resource that exists, and deleting it changes nothing. In this run it has no unit; its body, type and resource id are listed as settings for a person.
The linked storage setting is not tied
The update sets a TLS minimum on the storage account, but the run lists it as read by no unit. Decide and carry over that setting by hand.
Types the run does not map
This run shows types it covers. For an azapi type you use that does not appear in the output, look it up in the run's list of units and support resources before you assume it was mapped.
Questions platform teams ask
Do we need Azure credentials?
No. The StackToIT documentation states that it reads the Terraform and does not connect to an Azure account.
Does it read azapi as well as azurerm?
Yes. The docs name both providers, and this run assessed azapi resources for Service Bus and Application Insights next to azurerm resources.
Why does the run mention a lock file?
Without a lock file entry the run cannot prove which provider version the plan would use, so it caps confidence at MEDIUM and says so for the whole estate once.
Can we apply the draft directly?
No. It is a proposal for review. Fill the variables, plan in a test project, and let an architect decide the open settings first.
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.