Introduction
A landing zone is never finished when the first deployment is green. Modules get new versions, AWS changes defaults, and someone has to add the next account on a Tuesday afternoon. Day 2 is where you spend most of the years, and it is the part that feature comparisons skip.
In Part 1 I covered the bootstrap, in Part 2 logging and security. This last part is about living with both platforms, and about the decision I made: how a change flows, how I keep NTC current (I did a full module upgrade round in October 2026), how I give an AI agent enough context to help, what it costs, and then the conclusion. Let's dive in.
How a change flows
LZA: one pipeline
Every change to the configuration repository runs the same pipeline: validate, build, deploy. 40 to 75 minutes, whether you edited one line or twenty. That changes how you work. I batch changes, double-check before I push, and use the waiting time for something else. If the fix lives in a CDK customization, add the cdk synth step, commit the generated template, and an emergency change takes well over an hour.
The upside is a single place where everything is validated together, and a pipeline that orders itself through CloudFormation dependencies and runOrder. The aws-luminarlz-cli from Part 1 can run single stages and takes some of the pain out of it.
NTC: a pull request per phase
In NTC every phase has the same workflow. A pull request runs tofu plan, a merge to main runs tofu apply, and you can also start it manually. Authentication is OIDC, and the shared workflow runs OPA policy checks. A phase takes a few minutes, so I can try something, review the plan, and fix it within the same hour. Several fix commits in one afternoon are normal, not a sign that something is wrong.
There is exactly one place where one phase starts another:
flowchart LR
A["Phase 2: push to main"] --> B["tofu apply"]
B -->|"only if the apply succeeded"| C["Phase 3: workflow_dispatch"]
B -.->|"if the baseline config changed"| D["EventBridge starts the baseline pipelines"]After every successful push to Phase 2, a job dispatches the Phase 3 workflow and waits for it, so a Phase 2 run is not finished until Phase 3 is. On pull requests, it is skipped, and it is skipped when the apply fails. The Phase 3 run is a normal apply of unchanged code, usually "No changes". The second trigger is on the AWS side: when a Phase 2 apply changes the baseline configuration, an EventBridge rule starts the baseline pipelines in the member accounts within seconds.
No other phase chains anything. The rest are data dependencies through the parameter bucket, and you apply them in the right order yourself. A change in the log archive does not re-run security tooling, and the consumer only sees new parameters on its next run. That is why my Phase 5 repository has empty "trigger CI" commits in its history. I don't love that, but it is a pretty honest model: when a downstream phase should run, a human decided it.
Keeping NTC current
The first deployment is the easy part. Here is what keeping the organization up to date looked like in my case.
The bot bumps modules, not your config
In August 2026, Nuvibit's patcher bot opened dependency PRs against my phase repositories, and in some of them it moved a module to a new major version while the old providers blocks stayed. The lesson is to always re-read main before you plan an update, because your local checkout is stale the moment a bot has been around.
A major upgrade is a staged state migration
The NTC v2 account factory upgrade took about four hours and happened in September. The first step moved the account factory, lifecycle templates, and baseline templates to their 2.x and 4.x lines and put the baseline into plan-only mode, so I could review the state migration without touching any member account. Applying that first commit failed.
A few weeks later, Nuvibit added moved blocks that map the per-region baseline resources to a unified baseline keyed by region, dropped the import blocks that were no longer needed, and then plan-only was switched off so the migration was applied:
# illustrative, addresses simplified
moved {
from = module.baseline_eu_central_1.aws_s3_bucket.tfstate
to = module.baseline_unified["eu-central-1"].aws_s3_bucket.tfstate
}The pattern is the point: plan-only first, moved blocks for the state, remove obsolete imports, apply last.
The October round
On 20 October I brought all modules to their latest release in one day. One pull request per phase, in dependency order, each merged only after I had read its CI plan. These are the things that would have bitten me without that discipline:
- The plan of the account factory hides the real change. The baseline code runs inside the member accounts, so the PR plan only showed 12 in-place changes (zip hashes, pipeline policies, a few tags). I turned on
baseline_maintenance_plan_only = truefor one cycle, read the CodeBuild plans for all four core accounts, and only then switched it off. The real changes were moving Config recorders to the service-linked role and updating a few bucket and key policies. New module defaults also change things quietly, so I pinned two new settings (backup_support_enabledands3_abac_enabled) tofalse, because I don't use the backup template. - Coupled modules move together. The log archive 2.1 grants the Config service principal on its buckets, and the baseline templates 4.6 move the recorders to the service-linked role. Both affect the same delivery path, so I applied the log archive first, then the baseline right after. A short delivery gap is expected for recorders still on the old role.
- A new default can change a policy.
ntc-guardrail-templatesaddedwhitelist_for_specific_regionsin a minor release. By default, it opensus-east-1andus-west-2for a list of global services, which changed my existing workloads SCP. Setting it to{}keeps the old behavior. I accepted the new one, but only after reading the plan. - Module inputs change shape. In v2 the aliased
providers = { aws = aws.euc1 }is replaced by a plainregion = "eu-central-1"input (per bucket for the log archive). Apply provider v6 on the old module versions first, so the state already carries the region. - Check the unchanged
mainbefore you blame the upgrade. The security tooling plan showed SNS subscriptions and a GuardDuty setting changing. I opened a PR with an empty commit and got the identical plan on the old version—pre-existing drift, not the bump. - Look in the right places for what changed. JavaScript renders the release notes at docs.nuvibit.com, so a simple fetch returns an empty page, and the v1 to v2 migration guide did not cover every module. The module repositories are private, so I can't list tags either. I worked from the example config on the module pages.
If I had to condense it into a rule of thumb: one PR per phase, in dependency order, plan reviewed before merge, and anything stateful gets a plan-only cycle first.
LZA upgrades
LZA upgrades run through the same pipeline, so they inherit its rhythm: one change, one long end-to-end run, and a mistake costs another 45 to 75 minutes.
Giving an AI agent the context: a CLAUDE.md per phase
Seven repositories, large .tf files (25 to 53 KB each, because the modules ship long inline documentation), state spread over several accounts, and a deployment order that no single repository explains. That is a bad situation for anyone new to the setup, and an AI coding agent is always new to the setup.
So I created the context files myself:
- A root
AGENTS.md(imported byCLAUDE.md) with the phase order and dependency graph, the commands, the module list, the CI workflow, the gotchas, and where each phase keeps its state. - A
CLAUDE.mdin every phase repository: what the phase does, in which account it runs, how it is deployed, what it reads from and writes to the parameter bucket, and the traps.
A trimmed excerpt of the log archive's CLAUDE.md, to show the level of detail:
## Cross-Phase Dependencies
Writes (to the `log-archive` parameter node): bucket ARNs, bucket names,
KMS key ARNs. Consumed by Phase 1 (org CloudTrail), Phase 2 (Config
baseline) and Phase 5 (security tooling).
## Key Gotchas
- Runs in the Log Archive account, not the management account.
- Object Lock (COMPLIANCE) is commented out. Irreversible; enable it
only after testing.
- GuardDuty cannot export to opt-in regions.
- Do not enable access logging on the access logging bucket itself.
- The .tf files are large. Read specific sections, not the whole file.What goes into such a file is what the code can't tell you: the order, the account, the manual steps, the things that already went wrong once. Two habits make it worth the effort. Update the file in the same pull request as the change; otherwise it starts to lie. Keep account IDs and bucket names in a private repository only; that's why you don't see them in this series.
This is not an NTC feature. Nuvibit's blueprint repositories come with their own READMEs, and the context files are mine. The same idea works for an LZA configuration repository, and I would write one there too.
What it costs
The underlying AWS services are the same, so Security Hub, GuardDuty, and Config cost the same on both platforms. My full LZA setup ran at roughly USD 150 per month, with a budget alarm at USD 200. What differs is everything around the services:
| LZA | NTC | |
|---|---|---|
| License | None, open source | Commercial, pricing not public |
| Pipeline compute | Negligible next to the services | Negligible, GitHub Actions minutes |
| Support | Docs, GitHub issues, AWS support plan | Included, direct access to the engineers |
| Biggest hidden cost | Waiting for 40 to 75 minute runs, CDK knowledge for the gaps | Ordering phases, reviewing upgrades |
Pipeline cost is noise. The real cost is the time people spend waiting or debugging. That is where the vendor question lives.
Networking, which I skipped on purpose
Networking is the least developed area in both of my organizations, and I'd say that is a legitimate choice. My LZA network-config.yaml essentially deletes default VPCs and nothing else (transitGateways: [], vpcs: []). In NTC, Phase 6 is a scaffold: a repository, a backend, and a state bucket in the Connectivity account, and no connectivity module deployed yet.
Both platforms can do far more with Transit Gateway, IPAM, and DNS, but I haven't deployed them and won't pretend otherwise. My rule of thumb: get logging, security, and identity right first, and add networking when the first workload needs it. Trying to do everything at once slowed me down on both platforms.
The conclusion
If you made it this far, the short version is that both platforms get you to a secure, multi-account AWS organization. They differ in what they ask from you.
| Choose LZA if... | Choose NTC if... |
|---|---|
| You already run Control Tower and want to extend it | You are Terraform/OpenTofu-native |
| Your team is strong in CDK and CloudFormation | Fast feedback matters (minutes against 40 to 75 minutes) |
| A free, open-source foundation matters | You want an engineer from the vendor when you are stuck |
| Declarative CIS monitoring is a top priority | You want a central Security Hub policy and delegated-admin setup out of the box |
| You prefer one repository and one pipeline | You can manage several repositories and their order |
| You accept CDK for the gaps | GitOps through pull requests and OIDC is your standard |
Vendor support is worth more than the invoice shows
On Day 1 of my NTC deployment, an engineer from Nuvibit made direct commits to my repositories. During the v2 migration, they wrote the moved blocks. I can't put a euro figure on that, but for a small team deploying its first landing zone, it is the difference between days of searching and an afternoon. With LZA, when I needed Security Hub central configuration, it was me, the documentation, and several 45-minute pipeline runs. For a large team with deep AWS knowledge and a platform group, the calculation can go the other way.
Lock-in is real on both sides
A common argument is that NTC's lock-in is small, because it is plain HCL. After a year with it, I'd put it differently. The modules are in private repositories, and even tofu init needs GitHub App authentication to fetch them. Your code is HCL, but the building blocks aren't yours. On the LZA side, AWS has a history of retiring solutions, and the enhancement layer on top of Control Tower would need a migration if it ever went away.
Neither risk disqualifies a platform. The AWS resources both create (organization, OUs, SCPs, Identity Center, Security Hub) are AWS-native and exist independently of either tool, so switching later is painful but possible. That is why I'd rather start now with the tool your team can adopt fastest than plan the perfect choice for another quarter.
What I'd do differently
LZA
- Plan for CDK from the first day. The YAML covers the basics and little else.
- Set up CIS monitoring early. It is one of the best things LZA offers.
NTC
- Draw the dependency graph before the first line of HCL, and deploy the log archive right after the account factory. However, the Nuvibit onboarding team will help you set this up.
- Delete the bootstrap IAM user, not just its key.
- Use the plan-only mode for the baseline from the very first run, and review the blueprint defaults before you accept them.
- Read the changelog and the plan before every module bump, and keep the vendor close in the first weeks.
- Write the context files for your agent as you go, not at the end.
Conclusion
Neither platform is better in absolute terms. LZA is the marathon: a declarative, AWS-native foundation with a slow loop, no license fee, and CDK for whatever the YAML can't do. NTC is the sprint with hurdles: a fast GitOps loop and a vendor you can call, in exchange for phases you have to order yourself and modules you can't read in public.
Personally, I decided to decommission LZA, and NTC is now my baseline and my new production. The organization I used for the partner test and kept current through the upgrade rounds above is the one that carries real work from here on. Next are the accounts still under the old LZA management account: in one of the next posts, I will migrate them into the NTC organization, and I will write down what breaks.
Why NTC for me, in one sentence: I prefer the fast feedback loop, OIDC without long-lived credentials, and an engineer I can reach, and I accept the phase ordering and the private modules as the price. Your answer can be different, and if you ask me which one to pick, I won't give a generic one. I'd ask what your team already knows, how quickly you want feedback, and how much outside help you want. If NTC is the answer and you want a hand with the move, that is what my AWS cloud migration including EU solution is for.
I hope this series helps you make a better decision for your own landing zone. If you have run one of them, or both, I'd love to hear how Day 2 looks for you. Happy building!
This post was written with AI assistance and verified plus enhanced by a human.



