AWS → STACKIT · ARCHITECTURE
From an AWS landing zone to a STACKIT organisation
An AWS landing zone is accounts, organizational units, service control policies, a shared network and an audit trail. STACKIT offers an organisation, folders, projects and a network area, but no policy-attachment layer. StackToIT assessed a compact landing zone: half of it mapped to Terraform, the other half came back as design work for your architects.
Three ways to lay out the organisation
The question is how much of the account boundary you need to keep. The run treats the AWS organisation, its units and its accounts as design work, not as resources to copy.
| Layout | How it maps | Trade-off |
|---|---|---|
| One organisation, folders, one project per account | An organizational unit becomes a folder; an account becomes a project. This is the comparable grouping the run proposes. | Closest to the AWS shape. The run notes that a project is not a separate account, billing and IAM boundary, so the access policy is re-created, not copied. |
| A separate organisation for accounts that need hard isolation | The run names this for accounts that need real isolation from the rest. | Strongest separation, but a second organisation means its own roles, its own network area and a second place to administer. |
| One project with several networks | Each subnet becomes a network in one project; the project is the boundary of the VPC. | Simplest for a single-VPC estate. Security groups are project-wide, so separation between tiers is kept with groups, not with accounts. |
Resource Manager roles manage the hierarchy and, per STACKIT’s documentation, do not grant access to cloud resources inside a project. Plan the role bindings per project explicitly.
What the run made of each landing-zone resource
Eleven units: 1 DIRECT, 5 RECONFIGURE, 5 REDESIGN. Five were drafted in Terraform, one needs no resource and five are left to an architect.
| AWS resource | STACKIT target | Class and confidence |
|---|---|---|
| aws_organizations_organization | None: the organisation is not created by the provider; roles are designed on organisation, folders and projects | REDESIGN, MEDIUM |
| aws_organizations_organizational_unit | Resource Manager folder, a grouping level with no policy attachment | REDESIGN, LOW |
| aws_organizations_account | Resource Manager project, a comparable container level | REDESIGN, LOW |
| aws_organizations_policy (service control) | None managed: policy as code in the delivery pipeline | REDESIGN, LOW |
| aws_vpc and aws_subnet | Networks in the project | RECONFIGURE, MEDIUM |
| aws_ec2_transit_gateway | Network area and network area region | RECONFIGURE, MEDIUM |
| aws_kms_key | KMS key ring and key | RECONFIGURE, MEDIUM |
| aws_s3_bucket (trail) | Object Storage bucket | DIRECT, MEDIUM |
| aws_cloudtrail (organisation trail) | Telemetry Router instance with an Object Storage destination | RECONFIGURE, MEDIUM |
| aws_guardduty_detector | None: detection is redesigned on Audit Log and Logs with a SIEM or third-party tool | REDESIGN, MEDIUM |
The mapping names Resource Manager folders and projects, but the draft writes no block for them. The landing-zone requirements file of the run lists both as mapped and not created.
Hub network and transit gateway
Input written for this page and assessed on 4 November 2026. Left: a VPC, a subnet and a transit gateway. Right: the network area, its region settings and the network the engine drafted, without the review comments above each block.
resource "aws_vpc" "hub" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "hub_a" {
vpc_id = aws_vpc.hub.id
cidr_block = "10.0.1.0/24"
availability_zone = "eu-central-1a"
}
resource "aws_ec2_transit_gateway" "hub" {
description = "shared hub"
}
resource "aws_ec2_transit_gateway_vpc_attachment" "hub" {
transit_gateway_id = aws_ec2_transit_gateway.hub.id
vpc_id = aws_vpc.hub.id
subnet_ids = [aws_subnet.hub_a.id]
}resource "stackit_network" "hub_a" {
project_id = var.project_id
name = "hub-a"
ipv4_prefix = "10.0.1.0/24"
ipv4_nameservers = var.default_ipv4_nameservers
}
resource "stackit_network_area" "migrated" {
organization_id = var.organization_id
name = "migrated-network-area"
}
resource "stackit_network_area_region" "migrated" {
organization_id = var.organization_id
network_area_id = stackit_network_area.migrated.network_area_id
ipv4 = {
network_ranges = [for p in var.network_area_ranges : { prefix = p }]
transfer_network = var.network_area_transfer_network
}
}The project is the VPC boundary
There is no VPC container. The subnet became a network in the project, and the transit gateway became a network area that routes privately between networks of the organisation.
Separation needs security groups
Networks in one area reach each other, and the area has no per-attachment object. A VPC that was not connected in AWS can reach its neighbour once both are in the area.
Values you supply
The area’s address ranges and the transfer network are variables, because the source does not state them.
Encryption key and organisation trail
The key and the trail are drafted; the connection of the audit stream is not. First pane: the key, the trail bucket and the trail. Then the key block from terraform/security.tf and the router blocks from terraform/observability.tf, without the review comments above each block. The S3 destination takes its endpoint and access key as variables.
resource "aws_kms_key" "logs" {
description = "log archive key"
enable_key_rotation = true
}
resource "aws_s3_bucket" "trail" {
bucket = "acme-org-trail"
}
resource "aws_cloudtrail" "org" {
name = "org-trail"
s3_bucket_name = aws_s3_bucket.trail.id
is_organization_trail = true
kms_key_id = aws_kms_key.logs.arn
}resource "stackit_kms_key" "logs" {
project_id = var.project_id
keyring_id = stackit_kms_keyring.migrated.keyring_id
display_name = "log-archive-key"
algorithm = "aes_256_gcm"
purpose = "symmetric_encrypt_decrypt"
protection = var.kms_protection
access_scope = var.kms_access_scope == "SNA" ? "SNA" : null
}resource "stackit_telemetryrouter_instance" "migrated" {
project_id = var.project_id
display_name = "migrated-telemetry-router"
}
resource "stackit_telemetryrouter_destination" "org" {
project_id = var.project_id
instance_id = stackit_telemetryrouter_instance.migrated.instance_id
display_name = "org-trail"
config = {
config_type = "S3"
s3 = {
bucket = stackit_objectstorage_bucket.trail.name
endpoint = var.trail_org_s3_endpoint
access_key = {
id = var.trail_org_s3_access_key_id
secret = var.trail_org_s3_secret_access_key
}
}
}
}Rotation is not carried
The provider documentation lists no rotation attribute for stackit_kms_key, so rotation becomes an operational process.
The trail’s scope is a link, not a flag
is_organization_trail has no counterpart. A person links the STACKIT organisation, folder or project to the router with a separate resource that the draft does not write.
The router handles STACKIT’s Audit Log
It delivers the audit events of STACKIT products. Per STACKIT’s documentation, admin activity is recorded by default and visible in the portal for 90 days; routing is how you keep it longer.
Key scope is your choice
access_scope comes from a variable with no default. The provider documentation gives PUBLIC as the default of the attribute, which makes the key reachable on the public KMS endpoint; SNA keeps it reachable over the network area only.
The structure the draft leaves to you
These blocks produced no Terraform. The organizational unit, the account and the policy each came back as REDESIGN with a reason, because their effect lives in policy and people, not in a resource. The policy attachment is assessed as part of the policy unit.
resource "aws_organizations_organizational_unit" "workloads" {
name = "workloads"
parent_id = aws_organizations_organization.main.roots[0].id
}
resource "aws_organizations_account" "prod" {
name = "acme-prod"
email = "aws-prod@example.com"
parent_id = aws_organizations_organizational_unit.workloads.id
}
resource "aws_organizations_policy" "deny_regions" {
name = "deny-non-eu-regions"
type = "SERVICE_CONTROL_POLICY"
content = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Deny"
Action = "*"
Resource = "*"
Condition = { StringNotEquals = { "aws:RequestedRegion" = ["eu-central-1", "eu-west-1"] } }
}]
})
}
resource "aws_organizations_policy_attachment" "workloads" {
policy_id = aws_organizations_policy.deny_regions.id
target_id = aws_organizations_organizational_unit.workloads.id
}How to decide
Count the real boundaries
List the accounts and ask what each one separates: billing, identity, blast radius, a regulator. A project covers some of these; a separate organisation covers more.
Move guardrails into the pipeline
An AWS service control policy limits what roles can do and grants nothing. On STACKIT, the same intent becomes checks in the pipeline that applies Terraform plus careful role design.
Decide the network area early
Ranges and the transfer network cannot be an afterthought: the STACKIT provider documentation states that a project is created in a network area by setting the label networkArea, and that this cannot be changed after the project is created.
Keep the audit scope explicit
Write down whether the trail covered one account, all regions or the whole organisation, then link the matching STACKIT scope to the router.
Plan detection separately
GuardDuty has no managed counterpart in the provider the run used. Decide which tool reads Audit Log and Logs.
Questions architects ask
Does a STACKIT project equal an AWS account?
Not exactly. The run maps an account to a project as a comparable container level and states that a project is not an equivalent account, billing and IAM boundary. Access is bound per project.
Can I keep my service control policies?
There is no managed policy layer in the provider version used. The run proposes policy as code in the pipeline and role design on the organisation, folders and projects.
What replaces the transit gateway?
A network area. Projects in the area connect through an internal transfer network, and routing tables update automatically. Traffic between regions currently goes over the public internet, according to STACKIT’s documentation.
Does StackToIT create the folders and projects?
No. The run lists them as mapped targets without a block in the draft. Your team creates them and sets the area label on each 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.