How to catch your infrastructure drift in minutes using driftctl.
Tags: devops, terraform, infrastructureascode, devsecops
Published: 2021-02-08 17:43:22


## **How to catch your infrastructure drift in minutes using driftctl.**

**Learn how to use driftctl in a real-life environment, with multiple Terraform states and output filtering.**

Whether it’s a script gone wild, a bad API call from a trusted Lambda, or just the daily SNAFU, you want to know about the situation.
Driftctl will do just that. In this guide, you will learn how to use [driftctl](https://github.com/cloudskiff/driftctl) an open source tool that tracks and warns of infrastructure drift, in a realistic real-life environment, with multiple Terraform states and output filtering. We will demonstrate how manual changes can impact drift detection and how driftctl complements Terraform plan!

![](https://cdn-images-1.medium.com/max/NaN/0*teDmTHYfb67b7lSL)

## Requirements

We recommend using an AWS account dedicated to testing.

* An AWS account with IAM keys ([AWS Homepage](https://aws.amazon.com/)). See [this AWS CLI documentation](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html) for a quick start.

* Terraform 0.14.x ([download link](https://www.terraform.io/downloads.html))

* Driftctl ([github](https://github.com/cloudskiff/driftctl))

* Example Terraform code for this lab ([here](https://github.com/cloudskiff/driftctl-advanced-aws-tutorial))

## Create a test AWS environment

Clone the example Terraform code and execute it with Terraform. This Terraform configuration will simply create a VPC and a basically locked down security group.

*Disclaimer: we used simple Terraform resources using the AWS provider: we did not try to create the most advanced, useful or complete Terraform configuration.*

    $ git clone 
    git@github.com:cloudskiff/driftctl-advanced-aws-tutorial.git
    $ cd driftctl-advanced-aws-tutorial

[Export your AWS variables](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html) (or `AWS_ACCESS_KEY_ID` / `AWS_SECRET_KEY` pair):

    $ export AWS_PROFILE="your-profile"

Initialize both Terraform environments : app_env_1 & app_env_2

    $ cd app_env_1
    $ terraform init
    [...]
    Terraform has been successfully initialized!

Run Terraform:

    $ terraform apply
    [...]
    Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
    $ cd ..

Do the same for the second environment:
```
    $ cd app_env_2

    $ terraform init
    [...]
    Terraform has been successfully initialized!
```
```
    $ terraform apply
    [...]
    Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
    $ cd ..
```

Congrats, your AWS account now includes:

* A VPC with a random name (from app_env_1)

* A security group with a single rule (from app_env_2)

## An overview of driftctl scanning options

As of this writing, driftctl can scan for AWS resources and complement Terraform in drift detection. More providers are on their way!

Using driftctl, you can:

* Select one or multiple Terraform states, locally or on S3 (with `--from <statefiles>`)

* Ignore some resources or fields by design (filling the `.driftignore`)

* Filter the output to choose only some resources or tags (with `--filter <expression>`)

* Format the output for readability or further processing (with `--output <format>`)

## Driftctl with multiple local Terraform states

The small lab we created above is simulating 2 different “environments” (in real life it can be different applications, environments, or teams), with distinct Terraform states.

If we run driftctl with a single state, resources from the other state will be detected as drifts, or more precisely, unmanaged resources, which is not true and not what we want.

Here’s how to use driftctl with multiple states:

    $ driftctl scan **--from** tfstate://./app_env_1/terraform.tfstate **--from** tfstate://./app_env_2/terraform.tfstate
    Scanning AWS on region: us-east-1
    Found 3 resource(s)
    - 100% coverage

## Create a Security Group drift and catch it

Let’s manually add a rule to the security group we created, so we can detect it using driftctl:

 1. Go to the [VPC Security Group](https://console.aws.amazon.com/vpc/home?#securityGroups:) in the AWS console.

 2. Select the security group named “Super Secure Security Group”

 3. Click on “Edit inbound rules” in the “Inbound rules” tab

 4. Click on “Add Rule”

* Select “All Traffic”, from “Anywhere”, add a random description

* Click on “Save Rules”

![](https://cdn-images-1.medium.com/max/2000/0*cz3Vxg_PRK3g6e_M)

Run Terraform from the folder where the security group is managed and confirm it doesn’t catch the manual change:

    $ cd env_app_2
    $ terraform apply
    aws_security_group.supersecure: Refreshing state... [id=sg-0d04a4ce8ff6a74d3]
    aws_security_group_rule.supersecure_sg_rule_1: Refreshing state... [id=sgrule-1254751605]
    Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Now run driftctl again:

    $ cd ..
    $ driftctl scan --from tfstate://./app_env_1/terraform.tfstate --from tfstate://./app_env_2/terraform.tfstate
    Scanning AWS on region: us-east-1
    **Found unmanaged resources:
    aws_security_group_rule:
    - sgrule-619247160 (Type: ingress, SecurityGroup: sg-0d04a4ce8ff6a74d3, Protocol: All, Ports: All, Source: ::/0)
    - sgrule-1309243877 (Type: ingress, SecurityGroup: sg-0d04a4ce8ff6a74d3, Protocol: All, Ports: All, Source: 0.0.0.0/0)
    **Found 5 resource(s)
    - 60% coverage
    - 3 covered by IaC
    - **2 not covered by IaC
    **- 0 deleted on cloud provider
    - 0/3 drifted from IaC

Holy cow, driftctl caught the drift!

![](https://cdn-images-1.medium.com/max/2000/0*teDmTHYfb67b7lSL)

## Ignore resources with .driftignore

In many cases, you’ll want driftctl to ignore some resources forever, to stop being notified about their existence, and also stop lowering the coverage score. Those reasons can include the very IAM key you use to run this lab (by definition it can’t be in this Terraform repo), resources that can’t or won’t be managed by Terraform, resources managed from teams sharing the same AWS account but not using Terraform, some legacy resources, or simply manual tests running a bit longer than expected. This is exactly the same approach as the usual `.gitignore` : you simply add to this file all the resources you want to be ignored.

## Create an unmanaged S3 bucket, catch it, ignore it.

To proceed with this step:

 1. Go to the [S3 dashboard](https://console.aws.amazon.com/s3)

 2. Manually create an S3 bucket that you’ll name however you like

Obviously, as this is completely done outside of its control, this bucket can’t be detected as a drift by Terraform, so we’re in the dark, as expected.

Confirm driftctl detects the manually created bucket:

    $ driftctl scan --from tfstate://./app_env_1/terraform.tfstate --from tfstate://./app_env_2/terraform.tfstate
    Scanning AWS on region: us-east-1
    Found unmanaged resources:
    [...]
    aws_s3_bucket:
    - randomBucket514
    [...]

Open the `.gitignore` file at the root of the repository, where we execute driftctl and add a line like `aws_s3_bucket` (in my case: `aws_s3_bucket.randomBucket514`) Now run driftctl again:

    $ driftctl scan --from tfstate://./app_env_1/terraform.tfstate --from tfstate://./app_env_2/terraform.tfstate
    [...]

Now the manually created S3 bucket is ignored forever!

## Output filters for better accuracy

It’s often helpful to be able to filter the output dynamically, to see results only for one type of resource (like only IAM users) or a specific tag (like only a specific environment).

Filtering in driftctl is implemented as in the AWS CLI: you won’t be lost! (hint: it’s JMESPath).

Let’s filter only VPC resources:

    $ driftctl scan --from tfstate://./app_env_1/terraform.tfstate --from tfstate://./app_env_2/terraform.tfstate **--filter "Type=='aws_vpc'"
    **Scanning AWS on region: us-east-1
    Found 1 resource(s)
    - 100% coverage
    Congrats! Your infrastructure is fully in sync.

Let’s now filter only for anything matching the “app_env_1” *“Environment”* tag on EC2:

    $ driftctl scan --from tfstate://./app_env_1/terraform.tfstate --from tfstate://./app_env_2/terraform.tfstate **--filter "Attr.Tags.Environment == 'app_env_1'"
    **Scanning AWS on region: us-east-1
    Found 1 resource(s)
    - 100% coverage
    Congrats! Your infrastructure is fully in sync.

You now can collect data only for the exact setup you want! Perfect for those reports

## JSON output manipulation

We’ve covered driftctl human-readable output, but it’s also often useful to process it further. That’s why driftctl can output to JSON!

Here’s how to generate a JSON file directly:

    $ driftctl scan --from tfstate://./app_env_1/terraform.tfstate --from tfstate://./app_env_2/terraform.tfstate **--output json://driftctl.json**

Take a look at the generated JSON file:

    $ cat output.json
    {
      "summary": {
      "total_resources": 5,
      "total_drifted": 0,
      "total_unmanaged": 2,
      "total_deleted": 0,
      "total_managed": 3
      },
    [...]
    $

Now you can process this file using a processor like jq, for example, to catch only the coverage percentage and maybe send it to a database, from which a graph can be generated:

    $ jq '.coverage' **< driftctl.json
    **60

The possibilities are now endless!

## Shutting down the lab

Don’t forget to destroy the resources we created for this lab:

    $ cd app_env_2; terraform destroy
    $ cd ../app_env_1; terraform destroy

Delete the S3 bucket you randomly created and your AWS account is now as clean as before.

## Recap

We covered a lot in this advanced tutorial:

* We used driftctl in combination with multiple Terraform state files.

* We detected manual drifts from Terraform-managed resources as well as entirely unmanaged resources.

* We manipulated driftctl output to ignore and filter resources based on type or tags.

* We used and processed driftctl JSON output.