Control Tower Customizations (CTC), also called CfCT, is the official way to deploy custom CloudFormation templates and Lambda-based controls across your organization. It’s powerful, but I’ve watched teams struggle when the deployment pipeline fails. You push code to your repository, the CodePipeline starts, it fails at the CodeBuild stage, and the error message is either cryptic or doesn’t show up at all. In this post, I’ll show you how to diagnose and fix CTC pipeline failures.
The Problem
The CTC pipeline has these stages: Source (CodeCommit) → Build (CodeBuild) → Deploy. The Build stage runs a Lambda function that validates your manifest.yaml and CloudFormation templates. If validation fails, the pipeline stops and your custom controls don’t deploy.
| Error Type | Description |
|---|---|
| Manifest validation failed | Invalid YAML syntax or incorrect structure |
| OU name not found | Manifest references an OU that doesn’t exist |
| Template not found | S3 key for CloudFormation template is incorrect |
| Permission denied | Deployment role lacks permissions for resources in template |
| Syntax error in template | CloudFormation template has invalid JSON or property |
Why Does This Happen?
manifest.yamlhas incorrect OU name or account email format — The manifest must reference OUs by their exact name (case-sensitive) and accounts by their email addresses. A typo will cause validation to fail.- CloudFormation template has syntax errors or invalid resource types — If your template has JSON syntax errors or references resources that don’t exist in the region you’re deploying to, the template fails validation.
- Deployment role lacks permissions — The
AWSControlTowerExecutionrole must have permissions to create/update all resources in your CloudFormation template. If the template creates S3 buckets, EC2 resources, or other services, the role needs those permissions. - S3 source bucket key for templates is incorrect — The manifest references template locations in S3. If the path doesn’t match exactly where you uploaded the template, the build fails.
- CodeBuild environment missing required environment variables — CTC relies on certain environment variables (like
MANIFEST_LOCATION) being set correctly. If these are misconfigured, the build fails.
The Fix
Start by checking the CodeBuild logs, then validate your manifest and templates.
Step 1: Check CodeBuild Logs
When the pipeline fails at the Build stage, go to the CodePipeline console:
CodePipeline → Custom-Control-Tower-CodePipeline → [Failed execution] → CodeBuild stage → CloudWatch Logs
Click the CloudWatch Logs link to see detailed error messages. This usually tells you exactly what’s wrong.
Step 2: Validate the manifest.yaml Syntax
Download your manifest.yaml and validate it. The manifest structure should look like this:
Metadata:
Version: 1.0
DeploymentTargets:
OrganizationalUnits:
- Name: "Core"
IncludeAccounts:
- "123456789012" # account ID, not email
- "210987654321"
Accounts:
- AccountId: "987654321098"
Email: "team@company.com"
Controls:
- Name: MyCustomControl
Description: "Custom control for compliance"
Input:
Parameters:
Param1: "Value1"
DeploymentTargets:
OrganizationalUnits:
- "Core"
Regions:
- "us-east-1"
- "us-west-2"
Check for:
- Correct OU names (case-sensitive, must match your Control Tower OU names exactly)
- Valid AWS account IDs (12 digits)
- Correct email addresses for accounts
- Valid regions
- Proper YAML indentation (no tabs, use spaces)
Step 3: Validate CloudFormation Templates
Test your template locally before uploading:
aws cloudformation validate-template \
--template-body file://my-template.json \
--region us-east-1
For YAML templates:
aws cloudformation validate-template \
--template-body file://my-template.yaml \
--region us-east-1
If validation succeeds, you see the template parameters and outputs. If it fails, fix the syntax errors.
Step 4: Verify Template S3 Path
The manifest references templates by their S3 location. For example:
DeploymentTargets:
Regions:
- "us-east-1"
Template:
Location: "s3://my-bucket/templates/my-control.json"
Ensure the template actually exists at that S3 path:
aws s3 ls s3://my-bucket/templates/my-control.json
If the file doesn’t exist, upload it:
aws s3 cp my-control.json s3://my-bucket/templates/my-control.json
Step 5: Test Manually Before Committing
Deploy the template manually to a test account first to verify it works:
aws cloudformation create-stack \
--stack-name test-custom-control \
--template-body file://my-template.json \
--region us-east-1
Monitor the stack creation:
aws cloudformation describe-stack-events \
--stack-name test-custom-control \
--region us-east-1
Once the stack creates successfully, you know the template is valid. Then commit to the CTC pipeline.
How to Run This
- Go to CodePipeline console → Custom-Control-Tower-CodePipeline.
- Find the failed execution and click into it.
- Navigate to the Build stage and click the CloudWatch Logs link.
- Read the error message — it tells you what’s wrong.
- If it’s a manifest error: fix the manifest.yaml and re-commit.
- If it’s a template error: run
aws cloudformation validate-templateto find syntax errors. - If it’s an S3 path error: verify the template exists at the S3 location in the manifest.
- Push the fixed code to CodeCommit.
- Monitor the pipeline — it should deploy successfully this time.
Is This Safe?
Yes. CTC is designed to be safe. It validates everything before deploying. If a deployment fails, it doesn’t roll out to any account. Test your templates locally first, then commit with confidence.
Key Takeaway
CTC pipeline failures are usually caused by manifest errors, CloudFormation syntax errors, or incorrect S3 paths. Check CloudBuild logs, validate your YAML and templates locally, verify S3 paths, and test manually before committing.
Have questions or ran into a different Control Tower issue? Connect with me on LinkedIn or X.