> For the complete documentation index, see [llms.txt](https://developer.harness.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.harness.io/continuous-delivery/use-continuous-delivery/provision-infrastructure/terraform-infra/remove-provisioned-infra-with-terraform-destroy.md).

# Remove Infrastructure with the Terraform Destroy step

Configure the Terraform Destroy step to remove provisioned infrastructure in your deployment pipeline.

The Terraform Destroy step removes provisioned infrastructure by running the `terraform destroy` command. You can use it to tear down infrastructure provisioned by Terraform Apply steps.

The Terraform Destroy step is independent of any other Terraform provisioning steps. It is not restricted to removing the infrastructure deployed in its stage. It can remove any infrastructure you have provisioned using Harness.

This topic explains how to configure the Terraform Destroy step in your deployment pipeline.

***

## What you will learn from this topic

* How to configure the [Terraform Destroy step](#configure-the-terraform-destroy-step) to remove provisioned infrastructure.
* How to use [Configuration Type](#configuration-type) to inherit from a Plan or Apply step or configure inline.
* How to use [Provisioner Identifiers](#provisioner-identifier) to link Destroy steps to infrastructure provisioned by Plan and Apply steps.
* How to preview [destroy plans](#preview-destroy-plans) before execution.

***

## Before you begin

* **Harness project access**: View, Create/Edit, and Execute permissions on Pipelines and Environments. For more information, refer to [RBAC in Harness](https://developer.harness.io/docs/platform/role-based-access-control/rbac-in-harness) to configure roles.
* **Terraform or OpenTofu installed on delegate**: Terraform or OpenTofu must be installed on the delegate to execute Terraform commands. For more information, refer to [Build custom delegate images with third-party tools](https://developer.harness.io/docs/platform/delegates/install-delegates/build-custom-delegate-images-with-third-party-tools) to install Terraform.
* **Provisioner Identifier**: You need the Provisioner Identifier from the Terraform Apply step that provisioned the infrastructure you want to destroy. For more information, refer to [Terraform Apply step](/continuous-delivery/use-continuous-delivery/provision-infrastructure/terraform-infra/run-a-terraform-plan-with-the-terraform-apply-step.md).
* **Understanding of Terraform concepts**: This guide assumes familiarity with Terraform workspaces, backends, modules, and state files. For more information, refer to [Terraform documentation](https://developer.hashicorp.com/terraform/docs).

***

## Preview destroy plans

To review what will be destroyed before executing the Terraform Destroy step, you can configure your pipeline with the following steps:

1. **Add a Terraform Plan step**: Configure the plan step with the `destroy` command. Fetch the configuration from a specific commit or branch in your repository. This allows you to view the destroy plan logs directly within the Harness UI.
2. **Add an Approval step**: Include an approval step after the Terraform Plan step to allow teams to review the destroy plan logs before proceeding with execution.
3. **Add a Terraform Destroy step**: Configure the destroy step with the `inline` command. Use the same commit or branch configuration as the Terraform Plan step. Execute the destroy step once the plan is reviewed and approved.

The destroy step behaves similarly to the apply step but is specifically designed for removing provisioned infrastructure. After running the destroy step, view the execution logs in the Harness pipeline execution details page to confirm the resources were destroyed.

***

## Configure the Terraform Destroy step

You can add the Terraform Destroy step in the following locations within your **Deploy** stage:

* **Execution steps**: Add it anywhere in the **Execution** section to destroy infrastructure as part of your deployment flow.
* **Dynamic Provisioning section**: Add it in the **Infrastructure** > **Dynamic Provisioning** section if you provisioned infrastructure dynamically in the same stage.
* **Rollback steps**: Add it in the **Rollback** section to clean up infrastructure if the deployment fails.

***

## Basic settings

Configure the following basic settings for the Terraform Destroy step.

### Name

In **Name**, enter a name for the step (for example, **destroy**).

The name is important because you can use it in [expressions](https://developer.harness.io/docs/platform/variables-and-expressions/harness-variables) to refer to settings in this step.

### Timeout

In **Timeout**, enter how long Harness should wait to complete the Terraform Destroy step before failing it.

### Configuration Type

Select how the Terraform Destroy step determines which resources to remove:

* **Inline**: Removes the provisioned resources you identify using **Provisioner Identifier** and configuration file settings. This is similar to running `terraform destroy` directly. When you select this option, the **Configuration File Repository** and **Optional Configuration** sections become visible, allowing you to configure the Terraform scripts and settings. For more information on these configuration options, refer to [Terraform Plan step configuration](/continuous-delivery/use-continuous-delivery/provision-infrastructure/terraform-infra/run-a-terraform-plan-with-the-terraform-plan-step.md#optional-configuration).
* **Inherit from Plan**: Removes the resources defined in the Harness Terraform Plan step that you identify using **Provisioner Identifier**. This is similar to `terraform plan -destroy` followed by applying the destroy plan.
* **Inherit from Apply**: Removes the resources defined in the Harness Terraform Apply step that you identify using **Provisioner Identifier**. This uses the state from the Apply step to determine what to destroy.

### Provisioner Identifier

In **Provisioner Identifier**, enter the same Provisioner Identifier you used in the Terraform Apply step that provisioned the resources you want to destroy.

The Provisioner Identifier enables Harness to locate the same Terraform state and configuration used during provisioning.

{% hint style="warning" %}
**Provisioner Identifier scope**

The Provisioner Identifier is project-wide. You can reference it across pipelines in the same project. Coordinate with your team to ensure identifiers are unique and not reused across pipelines to avoid conflicts.
{% endhint %}

***

## Command line options

{% hint style="info" %}
**Currently, the Terraform command line option is behind the feature flag `CDS_TERRAFORM_CLI_OPTIONS_NG`. Contact** [**Harness Support**](mailto:support@harness.io) **to enable the feature.**
{% endhint %}

This setting allows you to set the Terraform CLI options for Terraform commands depending on the Terraform step type.

For example: `-lock=false`, `-lock-timeout=0s`.

### Skip Terraform refresh

The Terraform refresh command will not run when this setting is selected.

***

## Advanced settings

In **Advanced**, you can use the following options:

* [Delegate Selector](https://developer.harness.io/docs/platform/delegates/manage-delegates/select-delegates-with-selectors)
* [Conditional Execution](https://developer.harness.io/docs/platform/pipelines/step-skip-condition-settings)
* [Failure Strategy](https://developer.harness.io/docs/platform/pipelines/failure-handling/define-a-failure-strategy-on-stages-and-steps)
* [Looping Strategy](https://developer.harness.io/docs/platform/pipelines/looping-strategies/looping-strategies-matrix-repeat-and-parallelism)
* [Policy Enforcement](https://developer.harness.io/docs/platform/governance/policy-as-code/harness-governance-overview)

***

## Working directory cleanup

Each Terraform step runs in a specific working directory on the delegate.

The Terraform working directory is located at `/opt/harness-delegate/./terraform-working-dir/`.

To that directory path, Harness adds additional directories that are named after the organization, account, project, and provisionerId (from the step) such that the final working directory is `/opt/harness-delegate/./terraform-working-dir/org-name/account-name/project-name/provisionerId/`.

In this final working directory, Harness stores the Terraform configuration and all fetched files such as var-files and backend-config.

Once the Terraform step execution is complete, Harness cleans up the main working directory `/opt/harness-delegate/./terraform-working-dir/`. The cleanup happens immediately after the step completes successfully or fails.

If you generate any local resources on the delegate in the directory where Terraform configurations are located, those resources are also removed. If you need those resources, make sure to generate them outside the Terraform working directory.

***

## YAML example

<details>

<summary>Terraform Destroy step YAML</summary>

**Inherit from Apply (most common):**

```yaml
- step:
    type: TerraformDestroy
    name: Terraform Destroy
    identifier: Terraform_Destroy
    timeout: 10m
    spec:
      provisionerIdentifier: my_terraform_provisioner
      configuration:
        type: InheritFromApply
      delegateSelectors:
        - my-delegate-selector
```

**Inline configuration (standalone):**

```yaml
- step:
    type: TerraformDestroy
    name: Terraform Destroy
    identifier: Terraform_Destroy
    timeout: 10m
    spec:
      provisionerIdentifier: my_terraform_provisioner
      configuration:
        type: Inline
        spec:
          configFiles:
            store:
              type: Github
              spec:
                connectorRef: GitHub_Connector
                gitFetchType: Branch
                branch: main
                folderPath: terraform/infrastructure
                repoName: my-org/my-terraform-repo
          varFiles:
            - varFile:
                type: Inline
                identifier: terraform_vars
                spec:
                  content: |
                    environment = "dev"
                    region      = "us-west-2"
          secretManagerRef: harnessSecretManager
      delegateSelectors:
        - my-delegate-selector
```

**In execution section (manual destroy with approval):**

```yaml
execution:
  steps:
    - step:
        type: HarnessApproval
        name: Approve Destroy
        identifier: Approve_Destroy
        spec:
          approvalMessage: Approve to destroy all provisioned infrastructure
          approvers:
            userGroups:
              - account._account_all_users
            minimumCount: 1
        timeout: 1d
    
    - step:
        type: TerraformDestroy
        name: Terraform Destroy
        identifier: Terraform_Destroy
        spec:
          provisionerIdentifier: my_terraform_provisioner
          configuration:
            type: InheritFromApply
          delegateSelectors:
            - my-delegate-selector
        timeout: 10m
```

**Key configuration:**

* `type: InheritFromApply`: Most common - destroys resources created by Terraform Apply step
* `type: Inline`: Standalone destroy with explicit Terraform configuration
* `provisionerIdentifier`: Must match the identifier used in Terraform Plan/Apply steps
* `delegateSelectors`: Use same selector as Plan/Apply for consistency

</details>

***

## Troubleshooting

<details>

<summary>Terraform destroy fails with state lock error</summary>

Ensure no other process is holding a state lock. Terraform automatically retries lock acquisition. Check backend configuration for lock timeout settings.

</details>

<details>

<summary>Permission denied when destroying infrastructure with Terraform Destroy step</summary>

Verify the connector used in the destroy step has permissions to delete the target resources. Check cloud provider IAM roles and policies.

</details>

<details>

<summary>Terraform destroy step reports resources still exist after completion</summary>

Check execution logs for partial destroy errors. Some resources may have dependencies preventing deletion. Review Terraform state file for remaining resources.

</details>

<details>

<summary>Destroy plan shows different resources than expected</summary>

Verify the Provisioner Identifier matches the Apply step that created the infrastructure. Check the Terraform configuration files are the same version used during provisioning.

</details>

***

## Next steps

* [Terraform Apply step](/continuous-delivery/use-continuous-delivery/provision-infrastructure/terraform-infra/run-a-terraform-plan-with-the-terraform-apply-step.md): Provision infrastructure with Terraform.
* [Terraform Plan step](/continuous-delivery/use-continuous-delivery/provision-infrastructure/terraform-infra/run-a-terraform-plan-with-the-terraform-plan-step.md): Preview infrastructure changes before applying or destroying.
* [Terraform Rollback step](/continuous-delivery/use-continuous-delivery/provision-infrastructure/terraform-infra/rollback-provisioned-infra-with-the-terraform-rollback-step.md): Roll back failed provisioning.

{% @harness-feedback/feedback %}
