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.yaml has 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 AWSControlTowerExecution role 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

  1. Go to CodePipeline consoleCustom-Control-Tower-CodePipeline.
  2. Find the failed execution and click into it.
  3. Navigate to the Build stage and click the CloudWatch Logs link.
  4. Read the error message — it tells you what’s wrong.
  5. If it’s a manifest error: fix the manifest.yaml and re-commit.
  6. If it’s a template error: run aws cloudformation validate-template to find syntax errors.
  7. If it’s an S3 path error: verify the template exists at the S3 location in the manifest.
  8. Push the fixed code to CodeCommit.
  9. 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.