This guide walks you through deploying AWS CloudFormation stacks using DeployHQ's [Custom Action](Article: custom-action) protocol. Custom Actions run CLI tools inside Docker containers during your deployment pipeline, letting you manage AWS infrastructure directly from DeployHQ.

## Prerequisites

Before you begin, ensure you have:

* **Beta Features Enabled:** Custom Actions require beta features to be enabled in your [account settings](Article: #682)
* **A Repository with CloudFormation Templates:** Your `.yaml` or `.json` CloudFormation template files stored in your repository
* **AWS IAM Credentials:** An access key and secret key for an IAM user with permissions to manage CloudFormation stacks

## Setting Up the Server

1. Open your project in DeployHQ and navigate to **Servers**.
2. Click **New Server** and select **Custom Action** as the protocol.
3. Under **Image Source**, select **Curated Template**.
4. Choose **AWS CLI** from the template dropdown. This sets the Docker image to `amazon/aws-cli:latest`.
5. Enter your deployment commands in the **Commands** field:

   ```text
   aws cloudformation deploy \
     --template-file /data/cloudformation/template.yaml \
     --stack-name my-app-stack \
     --capabilities CAPABILITY_IAM \
     --no-fail-on-empty-changeset
   ```

6. Set **Halt on error** to stop the deployment if the stack update fails.
7. Click **Create Server**.

## Configuring Credentials

Add the following [environment variables](Article: #163) to your DeployHQ server:

| Variable | Value |
|----------|-------|
| `AWS_ACCESS_KEY_ID` | Your AWS access key |
| `AWS_SECRET_ACCESS_KEY` | Your AWS secret key |
| `AWS_DEFAULT_REGION` | Target region (e.g., `us-east-1`) |

### IAM Permissions

The IAM user needs at minimum the following permissions:

* `cloudformation:CreateStack`
* `cloudformation:UpdateStack`
* `cloudformation:DeleteStack`
* `cloudformation:DescribeStacks`
* `cloudformation:DescribeStackEvents`
* `cloudformation:CreateChangeSet`
* `cloudformation:ExecuteChangeSet`
* `cloudformation:DeleteChangeSet`
* `cloudformation:DescribeChangeSet`

Additionally, the user needs permissions for whatever resources your CloudFormation templates create (e.g., EC2, S3, RDS, Lambda).

## Example Commands

### Basic Stack Deploy

The `aws cloudformation deploy` command handles both creation and updates:

```text
aws cloudformation deploy \
  --template-file /data/cloudformation/template.yaml \
  --stack-name my-app-stack \
  --capabilities CAPABILITY_IAM \
  --no-fail-on-empty-changeset
```

The `--no-fail-on-empty-changeset` flag prevents the command from failing when there are no changes to deploy.

### Deploy with Parameters

Pass parameters to your CloudFormation template:

```text
aws cloudformation deploy \
  --template-file /data/cloudformation/template.yaml \
  --stack-name my-app-stack \
  --capabilities CAPABILITY_IAM \
  --no-fail-on-empty-changeset \
  --parameter-overrides \
    Environment=production \
    InstanceType=t3.medium \
    ImageTag=%revision%
```

You can use DeployHQ [text variables](Article: #165) like `%revision%` and `%branch%` in your parameter values.

### Deploy with Parameter File

For complex parameter sets, use a parameters file stored in your repository:

```text
aws cloudformation deploy \
  --template-file /data/cloudformation/template.yaml \
  --stack-name my-app-stack \
  --capabilities CAPABILITY_IAM \
  --no-fail-on-empty-changeset \
  --parameter-overrides file:///data/cloudformation/params/production.json
```

### Using Change Sets for Review

If you want to preview changes before applying, create a change set and then execute it:

```text
aws cloudformation create-change-set \
  --template-body file:///data/cloudformation/template.yaml \
  --stack-name my-app-stack \
  --change-set-name deploy-%revision% \
  --capabilities CAPABILITY_IAM

aws cloudformation wait change-set-create-complete \
  --stack-name my-app-stack \
  --change-set-name deploy-%revision%

aws cloudformation describe-change-set \
  --stack-name my-app-stack \
  --change-set-name deploy-%revision%

aws cloudformation execute-change-set \
  --stack-name my-app-stack \
  --change-set-name deploy-%revision%

aws cloudformation wait stack-update-complete \
  --stack-name my-app-stack
```

### Retrieve Stack Outputs

After deployment, you can display stack outputs in the deployment log:

```text
aws cloudformation deploy \
  --template-file /data/cloudformation/template.yaml \
  --stack-name my-app-stack \
  --capabilities CAPABILITY_IAM \
  --no-fail-on-empty-changeset

aws cloudformation describe-stacks \
  --stack-name my-app-stack \
  --query "Stacks[0].Outputs" \
  --output table
```

## Multi-Environment Usage

### Separate Stacks per Environment

Use different stack names and parameter files for each environment. Create separate DeployHQ servers for staging and production:

**Staging server commands:**

```text
aws cloudformation deploy \
  --template-file /data/cloudformation/template.yaml \
  --stack-name my-app-staging \
  --capabilities CAPABILITY_IAM \
  --no-fail-on-empty-changeset \
  --parameter-overrides file:///data/cloudformation/params/staging.json
```

**Production server commands:**

```text
aws cloudformation deploy \
  --template-file /data/cloudformation/template.yaml \
  --stack-name my-app-production \
  --capabilities CAPABILITY_IAM \
  --no-fail-on-empty-changeset \
  --parameter-overrides file:///data/cloudformation/params/production.json
```

### Dynamic Stack Names

Use DeployHQ text variables for dynamic stack names:

```text
aws cloudformation deploy \
  --template-file /data/cloudformation/template.yaml \
  --stack-name my-app-%server% \
  --capabilities CAPABILITY_IAM \
  --no-fail-on-empty-changeset
```

## Troubleshooting

**"No updates are to be performed":**

* This means your stack is already up to date. Using `--no-fail-on-empty-changeset` with `deploy` prevents this from being treated as an error.

**"InsufficientCapabilitiesException":**

* Add `--capabilities CAPABILITY_IAM` or `--capabilities CAPABILITY_NAMED_IAM` if your template creates IAM resources

**"Stack is in ROLLBACK_COMPLETE state":**

* The stack failed to create and cannot be updated. Delete the stack manually in the AWS Console and retry the deployment.

**"Template format error":**

* Validate your template locally with `aws cloudformation validate-template --template-body file://template.yaml`
* Ensure your YAML or JSON syntax is correct

**Credential errors:**

* Verify the `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_DEFAULT_REGION` environment variables are set correctly on your DeployHQ server
* Ensure the IAM user has the required permissions listed above

**Timeout errors:**

* CloudFormation stack operations can take a long time. Custom Actions have a 30-minute timeout per command.
* For stacks that take longer, consider splitting your infrastructure into nested stacks or separate deployments
