Scan
$ driftctl scan [OPTIONS]
$ driftctl scan --from tfstate+s3://terraform.tfstate --to github+tf --output console://
Description
Scan resources from the input Terraform statefile and compare it to your current profile infrastructure.
Options
Flag | Environment | Default |
---|---|---|
--from | DCTL_FROM | tfstate://terraform.tfstate |
--output | DCTL_OUTPUT | console:// |
--to | DCTL_TO | aws+tf |
--quiet | DCTL_QUIET | false |
--strict | DCTL_STRICT | false |
--deep | DCTL_DEEP | false |
--tf-provider-version | DCTL_TF_PROVIDER_VERSION | |
--driftignore | DCTL_DRIFTIGNORE | .driftignore |
--config-dir | DCTL_CONFIG_DIR | $HOME |
--only-managed | DCTL_ONLY_MANAGED | false |
--only-unmanaged | DCTL_ONLY_UNMANAGED | false |
--from
Currently, driftctl only supports reading IaC from a Terraform state. We are investigating to support the Terraform code as well, as a state does not represent an intention.
Multiple states can be read by passing --from
flags. You can also use glob patterns to match multiple state files at once.
When no state is specified for the scan, driftctl automatically reads your HCL files in the current directory as an attempt to find which state to use.
This feature currently only works with the following backends: local
, gcs
, s3
, azurerm
as well as cloud
configuration for Terraform Cloud.
If it doesn't succeed, driftctl fallback looking for a local terraform.tfstate
file.
Example:
# I want to read a local state and a state stored in an S3 bucket:
$ driftctl scan \
--from tfstate+s3://statebucketdriftctl/terraform.tfstate \
--from tfstate://terraform_toto.tfstate
# In a given local folder
# driftctl will recursively use all files under this folder.
#
# N.B. Symlinks under the root folder will be ignored.
# If the folder itself is a symlink it will be followed.
$ driftctl scan --from tfstate://my-states/directory
# Only match state files in that directory
$ driftctl scan --from tfstate://my-states/directory/*.tfstate
# Using glob pattern to recursively use any *.tfstate file.
$ driftctl scan --from tfstate://path/to/**/*.tfstate
$ driftctl scan --from tfstate+s3://example-bucket/*.tfstate
$ driftctl scan --from tfstate+gs://example-bucket/**/*.tfstate
$ driftctl scan --from tfstate+azurerm://example-container/states/*.tfstate
# This works for
# We also support HTTP(s) URLs with authentication
# the tool will fetch the file from the given URL
#
# You can use the --headers option if you need to add extra headers to the request (e.g: for authentication purposes)
$ driftctl scan --from tfstate+https://example.com/terraform.tfstate --headers 'Authorization=Basic ...; X-Custom-Header=value'
# You can also read the current state for a given workspace from Terraform Cloud
# Don't forget to provide your Terraform Cloud API token
# The Terraform Cloud integration supports both workspace ids, and workspace names
$ driftctl scan --from tfstate+tfcloud://$WORKSPACE_ID --tfc-token $TFC_TOKEN
$ driftctl scan --from tfstate+tfcloud://$ORGANIZATION_NAME/$WORKSPACE_NAME --tfc-token $TFC_TOKEN
# You can also read the current state for a given workspace from Terraform Enterprise by passing the tfc-endpoint value
# that's specific to your Org's Terraform Enterprise installation
# You can obtain your workspace id from the General Settings of the workspace
# Don't forget to provide your Terraform Enterprise API token
#
$ driftctl scan --from tfstate+tfcloud://$WORKSPACE_ID --tfc-token $TFC_TOKEN --tfc-endpoint 'https://tfe.example.com/api/v2'
# You can use azure blob storage too
# See below how to setup authentication for that backend
$ driftctl scan --to azure+tf --from tfstate+azurerm://my-container/terraform.tfstate
# Blob patterns are also supported
$ driftctl scan --to azure+tf --from tfstate+azurerm://my-container/states/**
Supported IaC sources
- Terraform state
- Local:
--from tfstate://terraform.tfstate
- S3:
--from tfstate+s3://my-bucket/path/to/state.tfstate
- GCS:
--from tfstate+gs://my-bucket/path/to/state.tfstate
- HTTPS:
--from tfstate+https://my-url/state.tfstate
- Terraform Cloud / Terraform Enterprise:
--from tfstate+tfcloud://WORKSPACE_ID
- Azure blob storage:
--from tfstate+azurerm://container-name/path/to/state.tfstate
You can use any unsupported backend by using terraform
to pipe your state in a file and then use this file with driftctl:
$ terraform state pull > state.tfstate
$ driftctl scan --from tfstate://state.tfstate
S3
driftctl needs read-only access so you could use the policy below to ensure minimal access to your state file.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::mybucket"
},
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::mybucket/path/to/my/key"
}
]
}
HTTP + GitLab
The HTTP backend supports the GitLab managed Terraform State using their API.
All you need is a GitLab repository that contains a Terraform state and an access token with the read_api
scope.
Here's what the command looks like:
$ GITLAB_TOKEN=<access_token> \
driftctl scan \
--from tfstate+https://gitlab.com/api/v4/projects/<project_id>/terraform/state/<path_to_state> \
--headers "Authorization=Bearer ${GITLAB_TOKEN}"
You can find more information about the GitLab managed Terraform State on the GitLab documentation website.
Azure blob storage
To be able to access state from azure blob storage, you have to define these two environment variables
$ export AZURE_STORAGE_ACCOUNT=...
$ export AZURE_STORAGE_KEY=...
$ driftctl scan --from tfstate+azurerm://my-container/terraform.tfstate
# You can also use inline flags
$ driftctl scan --azurerm-storage-account 'storageaccountname' --azurerm-account-key 'key' --from tfstate+azurerm://my-container/terraform.tfstate
You can find theses values in your Azure console here:
--output
driftctl supports multiple kinds of output formats and by default uses the standard output (console).
Environment: DCTL_OUTPUT
Console
Usage
$ driftctl scan
$ driftctl scan --output console://
$ DCTL_OUTPUT=console:// driftctl scan
driftctl can write to multiple outputs at once by passing multiple --output
flags.
Structure
Found missing resources:
aws_s3_bucket:
- driftctl-bucket-test-2
Found resources not covered by IaC:
aws_s3_bucket:
- driftctl-bucket-test-3
Found changed resources:
- driftctl-bucket-test-1 (aws_s3_bucket):
~ Versioning.0.Enabled: false => true
Found 3 resource(s)
- 33% coverage
- 1 covered by IaC
- 1 not covered by IaC
- 1 missing on cloud provider
- 1/1 changed outside of IaC
JSON
Usage
$ driftctl scan --output json:///tmp/result.json # Will output results to /tmp/result.json
$ driftctl scan --output json://result.json # Will output results to ./result.json
$ driftctl scan --output json://stdout # Will output results in json to stdout directly
$ DCTL_OUTPUT=json://result.json driftctl scan
Structure
{
"summary": {
"total_resources": 3,
"total_changed": 1,
"total_unmanaged": 1,
"total_missing": 1,
"total_managed": 1,
"total_iac_source_count": 1
},
"managed": [
// list of resources found in IaC and in sync with remote
{
"id": "driftctl-bucket-test-1",
"type": "aws_s3_bucket",
"source": { // Source encapsulates metadata explaining where the resource is coming from within an IaC
"source": "tfstate://terraform.tfstate", // Represents the Terraform state file
"namespace": "module", // If you use Terraform module, it will be displayed here
"internal_name": "my_name" // Represents the internal Terraform resource's name
}
}
],
"unmanaged": [
// list of resources found in remote but not in IaC
{
"id": "driftctl-bucket-test-3",
"type": "aws_s3_bucket"
}
],
"missing": [
// list of resources found in IaC but not on remote
{
"id": "driftctl-bucket-test-2",
"type": "aws_s3_bucket"
},
],
"differences": [
// A list of changes on managed resources
{
"res": {
"id": "driftctl-bucket-test-1",
"type": "aws_s3_bucket"
},
"changelog": [
{
"type": "update", // Kind of change, could be one of update, create, delete
"path": [
// Path of the change, sorted from root to leaf
"Versioning",
"0",
"Enabled"
],
"from": false, // Mixed type
"to": true // Mixed type
}
]
}
],
"coverage": 33,
"provider_name": "AWS",
"provider_version": "2.18.5",
// Scan duration in seconds
"scan_duration": 27,
"date": "2022-04-08T10:35:00Z"
}
HTML
Usage
You can now visualize your scan results in your browser with the HTML output:
$ driftctl scan --output html://output.html # Will output results to ./output.html
$ DCTL_OUTPUT=html://output.html driftctl scan
Screenshots
Computed Fields
From Terraform documentation, a computed
field is often used to represent values that are not user configurable or can not be known at time of terraform plan
or apply
.
Since those values are not known ahead of time from terraform point of view, it is obviously possible that the values displayed in a terraform state file are not up-to-date and may require a terraform refresh
.
Thus, it could be possible that driftctl finds drifts that are considered false positives because of those outdated values.
We decided to output computed fields and to display a message at the end of the scan to remind you of this behavior.
Found changed resources:
- eipalloc-0e2894d8ea42851df (aws_eip):
~ AssociationId: "" => "eipassoc-0ee67e1ca759733a2" (computed)
~ Instance: "" => "i-004611704837fd09a" (computed)
~ NetworkInterface: "" => "eni-0a62972b0471447f6" (computed)
~ PrivateDns: <nil> => "ip-172-31-40-4.eu-west-3.compute.internal" (computed)
~ PrivateIp: "" => "172.31.40.4" (computed)
Found 1 resource(s)
- 100% coverage
- 1 covered by IaC
- 0 not covered by IaC
- 0 missing on cloud provider
- 1/1 changed outside of IaC
You have diffs on computed fields, check the documentation for potential false positive drifts
--to
driftctl supports multiple providers. By default it will scan against AWS, but you can change this using --to
.
Usage
Environment: DCTL_TO
$ driftctl scan --to PROVIDER+TYPE
# examples:
$ driftctl scan --to aws+tf
$ DCTL_TO=github+tf driftctl scan
Supported Providers
driftctl supports these providers:
github+tf
aws+tf
gcp+tf
azure+tf
--quiet
This flag prevents stdout to be use for anything but the scan result.
--strict
When running driftctl against an AWS account, you may experience unnecessary noises with resources that don't belong to you. It can be the case if you have an organization account in which you will by default have a service-linked role associated to your account (e.g. AWSServiceRoleForOrganizations). For now, driftctl ignores those service-linked resources by default.
If you still want to include those resources in the report anyway, you can enable the strict mode.
For now, resources include:
- Service-linked AWS IAM roles, including their policies and policy attachments
Usage
# Enable the strict mode
$ driftctl scan --strict
--deep
⚠️ This is EXPERIMENTAL
Enabling deep mode while using a Terraform state as IaC source can lead to unexpected behaviors: false positive drifts, undetected drifts.
Deep mode enables resources details retrieval. It was the original driftctl behavior.
In deep mode we compare resources details to expected ones (like a terraform plan). In non-deep mode (the default one) we only enumerate resources and display which ones are out of IaC scope.
Since it overlaps the new terraform plan
behavior (as of Terraform 0.15 it shows diffs between your state and the remote) we moved the original behavior under the --deep
experimental flag.
If you use a version of driftctl prior to 0.13, the deep mode was the default behavior. If you want to keep the old behavior in a newer version you have to enable the deep mode flag.
Usage
# Enable the deep mode
$ driftctl scan --deep
--tf-provider-version
You can specify a terraform provider version to use. If none, driftctl uses defaults from the table below:
provider | version |
---|---|
aws | 3.19.0 |
github | 4.4.0 |
Usage
# I use terraform provider 3.43.0 so I can use this provider with driftctl to avoid scan errors
# driftctl will scan with an AWS terraform provider v3.43.0
$ DCTL_TF_PROVIDER_VERSION=3.43.0 driftctl scan
# Same parameter is used for every cloud provider
# driftctl will scan with a GitHub terraform provider v4.10.1
$ DCTL_TF_PROVIDER_VERSION=4.10.1 driftctl scan --to github+tf
--driftignore
The default name for a driftignore file is .driftignore
. If for some reason you want to use a custom filename, you can do so using the --driftignore
flag. This is especially useful when you have multiple driftignore files, where each of them represents a particular use case.
You can use only one driftignore file at once.
Usage
# Apply ignore directives from the /path/to/driftignore file
$ driftctl scan --driftignore /path/to/driftignore
--config-dir
You can change the directory path that driftctl uses for configuration. By default, it is the $HOME
directory.
This can be useful, for example, if you want to invoke driftctl in an AWS Lambda function where you can only use the /tmp
folder.
Usage
$ driftctl scan --config-dir path_to_driftctl_config_dir
$ DCTL_CONFIG_DIR=path_to_driftctl_config_dir driftctl scan
--tf-lockfile
By default, driftctl tries to read a Terraform lock file (.terraform.lock.hcl
) in the current directory, so driftctl can automatically detect which provider to use, according to the --to
flag. You can specify a custom path for that file using the --tf-lockfile
flag. If parsing the lockfile fails for some reason, errors will be logged and scan will continue.
Note that when using both --tf-lockfile
and --tf-provider-version
flags together, --tf-provider-version
will simply take precedence overall.
Usage
$ driftctl scan --to aws+tf --tf-lockfile path/to/.terraform.lock.hcl
--only-managed
This flag allows you to output only resources that are managed by your IaC.
Note that this flag will also output your missing resources in the scan report.
Please be aware that this flag relies on --deep
mode supported resources only.
Another limitation in its current state is that you will be subject to rate limits by your Cloud Provider since we still enumerate all resources before reading resources details.
Usage
$ driftctl scan --only-managed
Found missing resources:
- vol-1234567890 (aws_ebs_volume)
From tfstate://terraform.tfstate
- i-1234567890 (aws_instance.bar)
Found changed resources:
From tfstate://terraform.tfstate
- i-0987654321 (aws_instance.foo):
~ instance_type: "t2.nano" => "t2.micro"
Found 4 resource(s)
- 50% coverage
- 2 resource(s) managed by Terraform
- 1/2 resource(s) out of sync with Terraform state
- 2 resource(s) found in a Terraform state but missing on the cloud provider
--only-unmanaged
This flag allows you to output only resources that are not managed by your IaC.
Usage
$ driftctl scan --only-unmanaged
Found resources not covered by IaC:
aws_iam_policy_attachment:
- AWSServiceRoleForAPIGateway-arn:aws:iam::aws:policy/aws-service-role/APIGatewayServiceRolePolicy
- AWSServiceRoleForAWSCloud9-arn:aws:iam::aws:policy/aws-service-role/AWSCloud9ServiceRolePolicy
aws_iam_role:
- custom-role
Found 5 resource(s)
- 40% coverage
- 2 resource(s) managed by Terraform
- 3 resource(s) not managed by Terraform
Exit codes
If no drift is found and all infrastructure is in sync with the
infrastructure-as-code state (after accounting for filters and driftignore),
driftctl
will exit with status 0.
If drift is found, driftctl
will exit with status 1.
If any other error occurs, driftctl
will exit with status 2.