AZURE → STACKIT · COMPLETE GUIDE

The complete Azure to STACKIT migration guide

Start from the Terraform you already have for Azure. StackToIT reads it, classifies every resource, drafts the STACKIT Terraform it can and lists, per owner role, what only a person can decide. You then lay out the landing zone, apply in waves, move the data and cut over. This guide follows that order and shows a real run on a mixed estate of 11 resources.

The migration in eight steps

Each step names the file of the run that holds its answer. The files are in the output folder that the assess command writes.

  1. 1. Collect the Terraform

    Gather the root modules for AzureRM or AzAPI, with their variable files. A variable without a value leaves a unit without a draft, so hand the engine the same files your pipeline uses. StackToIT reads the code only and does not connect to an Azure account.

  2. 2. Run the assessment

    Run stacktoit assess on the folder. The console prints the source figures, how many units were drafted and how many open requests there are. Open index.html first.

  3. 3. Answer the first requests

    review/inputs.yaml lists the typed questions. One blocks the draft: the target region. Others, such as the allowed CIDRs of each managed service, are variables of the draft.

  4. 4. Review decisions by owner role

    review/decisions.yaml holds one record per owner role and unit. A review records that someone looked; it frees no step until the draft is regenerated with the answers.

  5. 5. Lay out the target

    Create the folders and the project, and a Network Area if networks must reach each other. migration/landing-zone-requirements.md lists what the organization has to provide before the first apply.

  6. 6. Apply in waves

    migration/technical-sequencing.md orders units by dependency. In the run below, the managed services and the network come first, the subnet next, the server after it and the data disk last, because it is attached to the server.

  7. 7. Move the data

    migration/data-movement.md lists every stateful unit with the mechanism UNKNOWN until a person chooses one. StackToIT never moves data.

  8. 8. Cut over and close the settings

    Switch the applications, then close or accept each open critical setting in execution/manual/actions.md. Keep the Azure side until the checks are done.

Step 2: run the assessment

A mixed demo estate written for this guide: a virtual network with one subnet, a Linux VM with a data disk, a Redis cache, a Service Bus namespace with a queue and a Cosmos DB account with the MongoDB API. The run is dated 4 November 2026. The summary lines are copied from the console of that run.

Commandterminal
stacktoit assess . ./out --offline --no-validate
Console summaryconsole
Source        11 resources · 7 units · visibility: Complete
Migration     6 drafted · 1 need no resource (of 7 units the draft considered)
Target        STACKIT Terraform generated
Attention     7 units to review · 5 open requests (1 block the draft, marked in inputs-template.yaml)
  • 11 resources, 7 units

    The queue folds into its namespace, the data disk attachment into the disk and the NIC into the VM; the resource group is not a unit. Six units were drafted and one needs no resource, the virtual network.

  • What the run created

    OpenTofu steps for 10 resource blocks, and 16 manual steps in 11 work packages for the 25 critical settings the draft does not carry.

Compute: a VM and its disk

Left: the Linux VM and the managed data disk. Right: the server and volume blocks the engine wrote; the review comments above each block are left out here.

Example inputmain.tf
resource "azurerm_linux_virtual_machine" "batch" {
  name                  = "vm-batch-01"
  location              = azurerm_resource_group.core.location
  resource_group_name   = azurerm_resource_group.core.name
  size                  = "Standard_D4s_v5"
  admin_username        = "azureuser"
  network_interface_ids = [azurerm_network_interface.batch.id]

  admin_ssh_key {
    username   = "azureuser"
    public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleKeyOnlyForTheDemo demo"
  }

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Premium_LRS"
    disk_size_gb         = 64
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "ubuntu-24_04-lts"
    sku       = "server"
    version   = "latest"
  }
}

resource "azurerm_managed_disk" "batch_data" {
  name                 = "disk-batch-data"
  location             = azurerm_resource_group.core.location
  resource_group_name  = azurerm_resource_group.core.name
  storage_account_type = "Premium_LRS"
  create_option        = "Empty"
  disk_size_gb         = 256
}
STACKIT Terraform by StackToITterraform/compute.tf
resource "stackit_server" "batch" {
  project_id        = var.project_id
  name              = "vm-batch-01"
  machine_type      = var.batch_machine_type
  availability_zone = var.availability_zone
  boot_volume = {
    source_type           = "image"
    source_id             = var.batch_image_id
    size                  = 64
    delete_on_termination = true
  }
  keypair_name       = stackit_key_pair.batch.name
  network_interfaces = [stackit_network_interface.batch_1.network_interface_id]
}

resource "stackit_volume" "batch_data" {
  project_id        = var.project_id
  name              = "disk-batch-data"
  availability_zone = var.availability_zone
  size              = 256
  performance_class = var.batch_data_performance_class
}
  • Size, image and login are variables

    The machine type and the image ID have no default. The Azure VM size does not map to a name; you choose a STACKIT machine type. The admin user azureuser is not carried: the login user is the one of the image.

  • Disk performance is a choice

    The boot volume keeps its size and loses caching and the Premium_LRS type; the data volume takes the 256 size as given and a performance class variable. Attach by volume ID in the guest, not by LUN, because the LUN is not carried.

  • Order matters

    The disk is the last unit, because it attaches to a server that must exist first.

Managed services: cache and messaging

From the same run. First pane: the Redis cache and the Service Bus namespace. Then the Valkey and RabbitMQ instances, one pane per draft file. The queue and the namespace settings are not generated.

Example inputmain.tf
resource "azurerm_redis_cache" "sessions" {
  name                = "redis-sessions-gwc"
  location            = azurerm_resource_group.core.location
  resource_group_name = azurerm_resource_group.core.name
  capacity            = 1
  family              = "C"
  sku_name            = "Standard"
  minimum_tls_version = "1.2"
}

resource "azurerm_servicebus_namespace" "events" {
  name                = "sb-events-gwc"
  location            = azurerm_resource_group.core.location
  resource_group_name = azurerm_resource_group.core.name
  sku                 = "Standard"
}
STACKIT: data.tfterraform/data.tf
resource "stackit_valkey_instance" "sessions" {
  project_id = var.project_id
  name       = "redis-sessions-gwc"
  plan_name  = var.sessions_plan_name
  version    = var.sessions_version
  parameters = {
    sgw_acl = join(",", var.sessions_allowed_cidrs)
  }
}
STACKIT: messaging.tfterraform/messaging.tf
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)
  }
}
  • Access lists have no safe default

    The draft sets the access list from variables without a default. The comment says that without a list STACKIT applies its default ACL, which covers the addresses of every STACKIT customer. List the client CIDRs before you plan.

  • A new client protocol

    Service Bus speaks AMQP 1.0. The run names an AMQP 0-9-1 client for RabbitMQ, so producers and consumers change their library, and the queue is recreated by hand.

  • The cache size is a plan

    Family and capacity are not carried. You choose the Valkey plan, and clients get new credentials; cached data is usually rebuilt, not migrated.

How the estate maps

Azure resourceSTACKIT targetMigration class
Linux virtual machine (with NIC)Compute Engine server and network interfaceReconfigure
Managed disk (with attachment)Block Storage volume attached to the serverReconfigure
Virtual networkSTACKIT project networking or Network AreaReconfigure
SubnetSTACKIT network with the same IPv4 prefixReconfigure
Azure Cache for RedisKey Value Store (Valkey) instance and credentialReconfigure
Service Bus namespace (with queue)RabbitMQ instanceReplatform
Cosmos DB account, MongoDB APIMongoDB Flex instanceReplatform

Classes as the run decided them: 5 RECONFIGURE and 2 REPLATFORM, all with MEDIUM confidence. Other services follow the same pattern; the Azure SQL, AKS and network guides show their own runs.

How the data moves, by service

  • MongoDB API

    The wire protocol is the same, so clients move with a new connection string, TLS CA and credentials. Data moves with mongodump and mongorestore. Global distribution, tunable consistency and request-unit scaling do not carry over.

  • Cache

    Cached data is usually rebuilt, not migrated. Only state you cannot rebuild, such as sessions you must keep, needs a plan.

  • Queues

    Drain or replay in-flight messages and recreate queues, exchanges and bindings in RabbitMQ. The run lists the namespace as a unit but not as stateful data.

  • Disks

    A managed disk becomes a volume of the same size. The data moves with the server image or with a file or block copy; the engine records the mechanism as undecided.

Decisions your architects own

  • Identity

    Who signs in to the new services. The run opens settings for the administrator login of the VM and the resource group scope of every unit; STACKIT has no resource groups, so map their boundaries to projects and roles.

  • Network exposure

    Whether each managed service is public or scoped to a Network Area, and which CIDRs may reach it.

  • Sizing

    Machine type, Valkey plan, MongoDB flavor and storage classes. None of these has a default in the draft.

  • Data movement

    Mechanism, window and tolerated downtime per stateful unit. The source states none of them.

  • Residency

    The location of each resource is an open setting; confirm the region and that the services exist in it.

Questions migration leads ask

Does StackToIT connect to my Azure subscription?

No. It reads Terraform and OpenTofu root modules written with the azurerm or azapi provider, offline if you ask for it. It does not read a live account, so resources created outside Terraform are not seen.

Can I apply the generated Terraform right away?

No. The package is a proposal for your architects to review. In this run none of the OpenTofu steps can run as written, because values such as project_id and the access lists are missing and settings are open.

Which Azure services have a managed STACKIT target?

Per the curated mapping: AKS to SKE, PostgreSQL and SQL Server to their Flex services, storage accounts to Object Storage, Key Vault to Secrets Manager and KMS, Redis to Valkey, Service Bus to RabbitMQ and Cosmos DB to MongoDB Flex or PostgreSQL Flex. App Service and Functions go to Cloud Foundry or SKE. Azure Firewall is decided case by case.

Where do I start inside the output folder?

Open index.html, then read engineering/architect-report.md for the findings per unit, review/inputs.yaml and review/decisions.yaml for the questions, and execution/README.md for the order of the steps.

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.