Template
The migration readiness map
A one-page map of every workload and data store before a move to an EU region, plus a Terraform policy that denies non-EU regions.
Moving out of us-east-1 is rarely hard technically. I'm starting to see the same failure in a lot of moves: a dependency nobody drew. A hard-coded region, a forgotten Lambda, a partner webhook pointed at an IP address. Every surprise in a migration is a line missing from the map.
The map has one row per workload and data store. Any cell that says 'not sure' is the next thing to find out. The region-deny policy at the end turns the target into a rule.
From the talk: Leaving us-east-1: moving a startup into the EU without downtime
The map
The first row is an example. The blank rows are for your own estate.
| Workload or data store | Region today | Owner | Depends on | Called from outside | Personal data | Size | Tolerable read-only window | Wave |
|---|---|---|---|---|---|---|---|---|
| orders-db (RDS PostgreSQL) | us-east-1 | Platform | api, billing-worker | No | Yes | 180 GB | 5 minutes | 3 |
| Blank row to fill in | ||||||||
| Blank row to fill in |
How I fill it: from the APIs, not from memory
- I list what runs where. AWS Resource Explorer with an aggregator index, or AWS Config. For example: aws resource-explorer-2 search --query-string "region:us-east-1".
- I map who calls whom. VPC flow logs, load balancer logs, and CloudTrail show the real traffic, including the forgotten calls.
- I find what calls in from outside. Webhooks, partner IP allow-lists, and mobile app builds with hard-coded endpoints.
- I search the code for the old region. Hard-coded region names and ARNs are the classic missing line.
- I order the waves. Low-risk, stateless workloads go first. Data stores with personal data and short read-only windows go last, after a rehearsal.
Choosing the EU region
I pick where customers, latency, and service availability point, not where the console opens.
| Cloud | Regions in the EU |
|---|---|
| AWS | Ireland (eu-west-1), Frankfurt (eu-central-1), Paris (eu-west-3), Stockholm (eu-north-1), Milan (eu-south-1), Spain (eu-south-2). London and Zurich are in Europe, but outside the EU. |
| Azure | West Europe sits in the Netherlands. |
| Google Cloud | europe-west4 sits in Eemshaven, the Netherlands. |
The region-deny policy
A service control policy that denies every region except the chosen EU ones. I attach it to a test OU first and to the workloads OU after.
# eu-only.tf: deny every AWS region except the EU ones you choose.
# Global services (IAM, Organizations, CloudFront, Route 53, and others) run in
# us-east-1, so they are exempt. Compare not_actions with AWS's current
# region-deny example before you attach it, and try it on a test OU first.
variable "allowed_regions" {
type = list(string)
default = ["eu-central-1", "eu-west-1"]
}
variable "target_id" {
description = "The OU (or account) the policy applies to. Never the root on day one."
type = string
}
data "aws_iam_policy_document" "eu_only" {
statement {
sid = "DenyOutsideAllowedRegions"
effect = "Deny"
resources = ["*"]
not_actions = [
"a4b:*", "acm:*", "aws-marketplace-management:*", "aws-marketplace:*",
"aws-portal:*", "budgets:*", "ce:*", "chime:*", "cloudfront:*", "config:*",
"cur:*", "directconnect:*", "ec2:DescribeRegions",
"ec2:DescribeTransitGateways", "ec2:DescribeVpnGateways", "fms:*",
"globalaccelerator:*", "health:*", "iam:*", "importexport:*", "kms:*",
"mobileanalytics:*", "networkmanager:*", "organizations:*", "pricing:*",
"route53:*", "route53domains:*", "route53-recovery-cluster:*",
"route53-recovery-control-config:*", "route53-recovery-readiness:*",
"s3:GetAccountPublic*", "s3:ListAllMyBuckets",
"s3:ListMultiRegionAccessPoints", "s3:PutAccountPublic*", "shield:*",
"sts:*", "support:*", "trustedadvisor:*", "waf-regional:*", "waf:*",
"wafv2:*", "wellarchitected:*",
]
condition {
test = "StringNotEquals"
variable = "aws:RequestedRegion"
values = var.allowed_regions
}
# Break-glass: the role Organizations creates in each account keeps access.
condition {
test = "ArnNotLike"
variable = "aws:PrincipalARN"
values = ["arn:aws:iam::*:role/OrganizationAccountAccessRole"]
}
}
}
resource "aws_organizations_policy" "eu_only" {
name = "eu-only-regions"
description = "Deny requests outside the approved EU regions"
type = "SERVICE_CONTROL_POLICY"
content = data.aws_iam_policy_document.eu_only.minified_json
}
resource "aws_organizations_policy_attachment" "eu_only" {
policy_id = aws_organizations_policy.eu_only.id
target_id = var.target_id
}What I leave out on purpose
I leave out re-architecting on the way, a multi-region active-active design, and a move to Kubernetes at the same time. Each one adds risk to the move without improving the residency answer. One change at a time keeps the rollback simple.
Next step
I highly recommend filling in the map for your three biggest data stores first. If any row says 'not sure', a thirty-minute call finds the missing lines before a customer does. Which data store worries your team most?
Book a callThese takeaways are an engineer's reading of the rules and the tools. I wrote them to help teams decide what to build. They aren't legal advice. I recommend checking the current text with your own counsel before planning around a date or a threshold.
Other takeaways from this talk
Runbook template
Cutover runbook
A one-page cutover runbook for moving data to an EU region: the countdown, go and no-go criteria, and a rollback with an owner per line.
Checklist
Residency evidence pack
The five attachments that answer most data-location questions in a security questionnaire, and how to keep them true every quarter.
Checklist
Old-region decommissioning checklist
What to find and delete in the old AWS region after a move to the EU: snapshots, vaults, log buckets, images, secrets, and the test stack.
Let's talk.
A 30-minute call, no pitch. We look at your current setup, your stage, and the thing that's bothering you about it. I give a straight opinion on what to change, what to leave alone, and whether the problem is the one it looks like. What's the one thing in your stack you'd fix tomorrow?
Prefer email or phone? hello@netninjas.eu or +31 6 45984313