AWS → STACKIT · CASE STUDY

The AWS Retail Store sample, assessed end to end

The Retail Store Sample App from AWS Containers is a public estate with five services on Amazon ECS and five managed data services behind them. StackToIT read it unchanged: 282 resources, 44 migration units, none DIRECT. This walkthrough follows the package from its first number to the decisions that decide the project, and states plainly what the run does not establish.

Proven on a real AWS estate

The AWS Retail Store Sample App, a public Terraform project from AWS, assessed by StackToIT and published in full, exactly as the run wrote it.

View Case Study
282
Terraform resource declarations analyzed
44
migration units identified
38
target units generated, from 52 considered
331
plan steps written

How to read the package

The order an architect would open the files in. Paths are relative to the output folder of the published run.

  1. Start with the summary and the first blocker

    index.html and executive/brief.md state the figures and name the first required action: allow calls to STACKIT APIs that are not generally available. Four steps wait for that answer.

  2. Read the classes

    assessment.json holds each unit with class, confidence and reason. Of 44 units, 34 are RECONFIGURE, 9 REPLATFORM and 1 REDESIGN. No unit is HIGH confidence, because the source provider is unpinned.

  3. Take the decisions

    executive/decisions.md counts 16 decisions to make: 9 plan decisions with named options, 5 manual actions and 2 plan statements; decisions.json holds the records. Among the plan decisions are data movement, the engine version downgrade, the ingress path and pod segmentation.

  4. Follow the sequence

    migration/technical-sequencing.md orders units into waves by dependency; migration/data-movement.md lists the six stateful units that need a method.

  5. Open the draft

    terraform/ holds 38 generated units. Every block carries comments for what was not carried, with a pointer into the engineering trace.

  6. Work the manual steps

    execution/manual/actions.md groups the manual work into packages with an owner, a condition for done, and a rollback line.

What the 44 units became

Present units only; eight more units depend on values OpenTofu decides at plan time and are counted separately.

AWS sourceSTACKIT targetClass
Amazon ECS cluster and five servicesSTACKIT Kubernetes Engine (SKE) clusterREPLATFORM (6 units)
Aurora clusters for catalog and ordersMariaDB (catalog) and PostgreSQL Flex (orders)REPLATFORM (2 units)
OpenSearch domainSTACKIT OpenSearchReplatform
DynamoDB table for cartsMongoDB Flex, or PostgreSQL JSONB as the other optionRedesign
ElastiCache replication groupKey Value Store (Valkey)Reconfigure
Amazon MQ brokerSTACKIT RabbitMQReconfigure
Application Load BalancerSTACKIT Application Load BalancerReconfigure
10 IAM rolesService accounts with project role assignmentsRECONFIGURE (10 units)
12 security groups, VPC, NAT gateway, elastic IPSecurity groups, networks, implicit outbound pathRECONFIGURE (15 units)
4 secrets, one KMS key, one log groupSecrets Manager, KMS, a Logs instanceRECONFIGURE (6 units)

By domain, the 44 units are 16 network, 15 identity, 6 compute, 5 data, 1 observability and 1 messaging.

Two data services, drafted side by side

Blocks from terraform/data.tf of the published run, without the comment lines above them. Left: the orders database, an Aurora cluster moved to PostgreSQL Flex. Right: the carts table, a DynamoDB table moved to MongoDB Flex as an alternative, not an equivalent.

Orders databaseterraform/data.tf
resource "stackit_postgresflex_instance" "dependencies_orders_rds_this" {
  project_id = var.project_id
  name       = "retail-store-ecs-orders"
  version    = "15"
  flavor_id  = "2.4"
  storage = {
    class = var.postgresflex_storage_class
    size  = var.orders_rds_storage_size
  }
  backup_schedule = "0 2 * * *"
  network = {
    acl          = coalescelist(var.orders_rds_allowed_cidrs, stackit_ske_cluster.retail_app_ecs_cluster.egress_address_ranges)
    access_scope = var.orders_rds_access_scope == "SNA" ? "SNA" : null
  }
  retention_days = 32
}
Carts tableterraform/data.tf
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
  }
}
  • Sizes derived from the source

    The plan and flavor follow from the source’s size. The run raises a decision for it, with the default marked as unconfirmed.

  • Access lists

    The orders database admits the cluster’s outbound address ranges unless you supply CIDRs, so the database block depends on the cluster resource.

  • DynamoDB is a redesign

    Key design, capacity modes, streams and conditional writes do not carry over. The data access layer is rewritten against the MongoDB API, and the data moves by export and import.

Compute and logging

The cluster that replaces the ECS services and the Logs instance for the task logs. The cluster’s node pool sizes are variables.

SKE clusterterraform/kubernetes.tf
resource "stackit_ske_cluster" "retail_app_ecs_cluster" {
  project_id = var.project_id
  name       = "reta-7b7ce2"
  node_pools = [{
    name               = "workloads"
    machine_type       = var.cluster_machine_type
    minimum            = var.cluster_nodes_min
    maximum            = var.cluster_nodes_max
    availability_zones = var.availability_zones
  }]
}
Logs instanceterraform/observability.tf
resource "stackit_logs_instance" "logs_retention_180d" {
  project_id     = var.project_id
  display_name   = "logs-retention-180d"
  retention_days = 180
}
  • The workloads are manifests

    terraform/kubernetes/ holds a manifest set per service. The cluster’s outbound address ranges are computed, so the managed services’ access lists wait for them.

  • 180 days is the ceiling

    The source log group keeps its logs for ever, which a Logs instance cannot do. The run states that older logs are not kept and raises the archive decision.

The decisions that decide the project

  • How the data moves

    Six stateful units need a method: export and import, engine replication or an own tool. The source states no size, change rate or tolerated downtime, so the run chooses no mechanism.

  • Catalog on OpenSearch

    The catalog service depends on OpenSearch 3.3 while the region offered version 2. Options: move the data into the offered version 2, run the engine yourself on SKE, or wait. Until then the application is blocked.

  • Carts and checkout

    DynamoDB has no counterpart, so the services that use it need code changes. Checkout also shares the TLS-only Valkey endpoint.

  • Ingress path

    Keep a NodePort with the load balancer on node IPs, run an ingress controller, or use the load balancer extension for SKE. Each changes what your team operates.

  • Pod segmentation

    Security groups at node level are coarser than the source’s per-service groups. NetworkPolicies are written by a person.

  • Public endpoints and fixed egress

    Managed services default to public endpoints behind access lists. The NAT gateway’s fixed address is lost unless servers get their own public IP.

What this run does not establish

Stated by the run itself, not added afterwards.

  • The source was not validated

    The upstream estate has no provider lock file, so OpenTofu validation of the source did not run and every unit is capped at MEDIUM confidence.

  • Nothing was planned or applied on STACKIT

    The draft passed static validation against the provider schema. No plan ran against a live project, and no unit is cleared for automation.

  • No cost comparison

    The source names no AWS region, so no AWS price was fetched. Five of fourteen STACKIT lines are priced, which is a partial figure and not a saving.

  • No application is ready

    Zero of five applications are ready: catalog is blocked, carts and checkout need a redesign, and orders and ui are not ready because environment values or the workload identity are still open.

Questions about this case study

Is the Retail Store estate edited?

No. The run assessed the ECS Terraform root as published upstream, together with the local modules it calls. The source code is not republished; the linked repository holds it.

Why is nothing DIRECT?

Every present unit needs at least a configuration change, such as identity, networks or endpoints. The nearest to a lift is the group of RECONFIGURE units.

How many steps does the plan contain?

331 plan steps: 53 OpenTofu steps, 4 STACKIT API steps behind an opt-in, 272 manual steps and 2 refused. None of the 57 automated steps runs as written: 48 are free of decisions and manual prerequisites, and all 48 wait for a value such as the project ID.

Can I check the package myself?

Yes. The output is published unchanged with its manifest, and the verify step confirms that all 145 files match it by size and SHA-256. For your own estate, an assessment of its Terraform produces the same kind of package.

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.