Skip to main content
Version: 0.29.0

Authentication

To use driftctl, we need credentials to make authenticated requests to AWS. Just like the AWS CLI, we use credentials and configuration settings declared as user environment variables, or in local AWS configuration files.

driftctl supports named profile. By default, the CLI uses the settings found in the profile named default. You can override an individual setting by declaring the supported environment variables such as AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_PROFILE ...

If you are using an IAM role as an authorization tool, which is considered a good practice, please be aware that you can still use driftctl by defining a profile for the role in your ~/.aws/config file.

[profile driftctlrole]
role_arn = arn:aws:iam::123456789012:role/<NAMEOFTHEROLE>
source_profile = user # profile to assume the role
region = eu-west-3

You can now use driftctl by overriding the profile setting.

$ AWS_PROFILE=driftctlrole driftctl scan

Custom credentials to read a state on an S3 backend

If you want to use a different set of AWS credentials to read your state on S3, you can override each specific AWS environment variable with the DCTL_S3_ prefix. The purpose here is to have the choice to read a state in a different region than your infrastructure. Please don't forget to use your usual AWS credentials to read the resources of your actual infrastructure.

# Export a dedicated AWS named profile (or any other AWS environment variables) to read your state in your S3 backend
$ export DCTL_S3_PROFILE="s3reader"
# Export the usual driftctl AWS named profile
$ export AWS_PROFILE="driftctlrole"
$ driftctl scan --from tfstate+s3://mybucket/terraform.tfstate

# You can also use a specific region to authenticate to the S3 bucket
$ DCTL_S3_REGION=us-east-1 driftctl scan --from tfstate+s3://mybucket/terraform.tfstate

Terraform custom role

You will find below our custom role that you can assume to run driftctl written in HCL.

data "aws_caller_identity" "current" {}

data "aws_iam_policy_document" "assume_role_policy" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]
}
}
}

data "aws_iam_policy_document" "policy" {
statement {
effect = "Allow"
actions = [
"apigateway:GET",
"cloudformation:DescribeStacks",
"cloudformation:GetTemplate",
"cloudfront:GetDistribution",
"cloudfront:ListDistributions",
"cloudfront:ListTagsForResource",
"ec2:DescribeAddresses",
"ec2:DescribeImages",
"ec2:DescribeInstanceAttribute",
"ec2:DescribeInstances",
"ec2:DescribeInstanceCreditSpecifications",
"ec2:DescribeInternetGateways",
"ec2:DescribeKeyPairs",
"ec2:DescribeNetworkAcls",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSnapshots",
"ec2:DescribeTags",
"ec2:DescribeVolumes",
"ec2:DescribeVpcs",
"ec2:DescribeVpcAttribute",
"ec2:DescribeVpcClassicLink",
"ec2:DescribeVpcClassicLinkDnsSupport",
"ec2:DescribeSubnets",
"ec2:DescribeNatGateways",
"ec2:DescribeLaunchTemplates",
"ec2:GetEbsEncryptionByDefault",
"ecr:DescribeRepositories",
"ecr:ListTagsForResource",
"elasticache:DescribeCacheClusters",
"iam:GetPolicy",
"iam:GetPolicyVersion",
"iam:GetRole",
"iam:GetRolePolicy",
"iam:GetUser",
"iam:GetUserPolicy",
"iam:ListAccessKeys",
"iam:ListAttachedRolePolicies",
"iam:ListAttachedUserPolicies",
"iam:ListPolicies",
"iam:ListRolePolicies",
"iam:ListRoles",
"iam:ListUserPolicies",
"iam:ListUsers",
"iam:ListGroups",
"iam:ListGroupPolicies",
"kms:DescribeKey",
"kms:GetKeyPolicy",
"kms:GetKeyRotationStatus",
"kms:ListAliases",
"kms:ListKeys",
"kms:ListResourceTags",
"lambda:GetEventSourceMapping",
"lambda:GetFunction",
"lambda:GetFunctionCodeSigningConfig",
"lambda:ListEventSourceMappings",
"lambda:ListFunctions",
"lambda:ListVersionsByFunction",
"rds:DescribeDBInstances",
"rds:DescribeDBSubnetGroups",
"rds:ListTagsForResource",
"route53:GetHostedZone",
"route53:ListHostedZones",
"route53:ListResourceRecordSets",
"route53:ListTagsForResource",
"route53:ListHealthChecks",
"route53:GetHealthCheck",
"s3:GetAccelerateConfiguration",
"s3:GetAnalyticsConfiguration",
"s3:GetBucketAcl",
"s3:GetBucketCORS",
"s3:GetBucketLocation",
"s3:GetBucketLogging",
"s3:GetBucketNotification",
"s3:GetBucketObjectLockConfiguration",
"s3:GetBucketPolicy",
"s3:GetBucketRequestPayment",
"s3:GetBucketTagging",
"s3:GetBucketVersioning",
"s3:GetBucketWebsite",
"s3:GetEncryptionConfiguration",
"s3:GetInventoryConfiguration",
"s3:GetLifecycleConfiguration",
"s3:GetMetricsConfiguration",
"s3:GetReplicationConfiguration",
"s3:ListAllMyBuckets",
"s3:ListBucket",
"s3:GetBucketPublicAccessBlock",
"sqs:GetQueueAttributes",
"sqs:ListQueueTags",
"sqs:ListQueues",
"sns:ListTopics",
"sns:GetTopicAttributes",
"sns:ListTagsForResource",
"sns:ListSubscriptions",
"sns:ListSubscriptionsByTopic",
"sns:GetSubscriptionAttributes",
"dynamodb:ListTables",
"dynamodb:DescribeTable",
"dynamodb:DescribeGlobalTable",
"dynamodb:ListTagsOfResource",
"dynamodb:DescribeTimeToLive",
"dynamodb:DescribeTableReplicaAutoScaling",
"dynamodb:DescribeContinuousBackups",
"rds:DescribeDBClusters",
"rds:DescribeGlobalClusters",
"application-autoscaling:DescribeScheduledActions",
"autoscaling:DescribeLaunchConfigurations",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeListeners"
]
resources = ["*"]
}
}

resource "aws_iam_role" "driftctl_assume_role" {
name = "driftctl_assume_role"
assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json
}

resource "aws_iam_role_policy" "driftctl_policy" {
name = "driftctl_policy"
role = aws_iam_role.driftctl_assume_role.id
policy = data.aws_iam_policy_document.policy.json
}

CloudFormation template

Deploy this CloudFormation template to create our limited permission role that you can use as per our above authentication guide.

Launch Stack

Once the stack is deployed, you need to attach the following policy to your IAM User which will allow him to assume only the role. For more information about granting a user access to assume a role, see the official IAM User Guide.

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::<IDOFYOURACCOUNT>:role/driftctl_assume_role"
}
]
}

Update the CloudFormation template

It does not exist an automatic way to update the CloudFormation template from our side because you launched this template on your AWS account. That's why you must be the one to update the template to be on the most recent driftctl role.

Find below two ways to update the CloudFormation template:

  1. With the AWS console
  • In the AWS CloudFormation console, from the list of stacks, select the driftctl stack
  • In the stack details pane, choose Update
  • Select Replace current template and specify our Amazon S3 URL https://driftctl-cfn-templates.s3.eu-west-3.amazonaws.com/driftctl-role.yml, click Next
  • On the Specify stack details and the Configure stack options pages, click Next
  • In the Change set preview section, check that AWS CloudFormation will indeed make changes
  • Since our template contains one IAM resource, select I acknowledge that this template may create IAM resources
  • Finally, click Update stack
  1. With the AWS CLI
$ aws cloudformation update-stack --stack-name DRIFTCTL_STACK_NAME --template-url https://driftctl-cfn-templates.s3.eu-west-3.amazonaws.com/driftctl-role.yml --capabilities CAPABILITY_NAMED_IAM

Least privileged policy

driftctl needs access to your cloud provider account so that it can list resources on your behalf.

As AWS documentation recommends, the below policy is granting only the permissions required to perform driftctl's tasks.

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Resource": "*",
"Action": [
"apigateway:GET",
"cloudformation:DescribeStacks",
"cloudformation:GetTemplate",
"cloudfront:GetDistribution",
"cloudfront:ListDistributions",
"cloudfront:ListTagsForResource",
"ec2:DescribeAddresses",
"ec2:DescribeImages",
"ec2:DescribeInstanceAttribute",
"ec2:DescribeInstances",
"ec2:DescribeInstanceCreditSpecifications",
"ec2:DescribeInternetGateways",
"ec2:DescribeKeyPairs",
"ec2:DescribeNetworkAcls",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSnapshots",
"ec2:DescribeTags",
"ec2:DescribeVolumes",
"ec2:DescribeVpcs",
"ec2:DescribeVpcAttribute",
"ec2:DescribeVpcClassicLink",
"ec2:DescribeVpcClassicLinkDnsSupport",
"ec2:DescribeSubnets",
"ec2:DescribeNatGateways",
"ec2:DescribeLaunchTemplates",
"ec2:GetEbsEncryptionByDefault",
"ecr:DescribeRepositories",
"ecr:ListTagsForResource",
"elasticache:DescribeCacheClusters",
"iam:GetPolicy",
"iam:GetPolicyVersion",
"iam:GetRole",
"iam:GetRolePolicy",
"iam:GetUser",
"iam:GetUserPolicy",
"iam:ListAccessKeys",
"iam:ListAttachedRolePolicies",
"iam:ListAttachedUserPolicies",
"iam:ListPolicies",
"iam:ListRolePolicies",
"iam:ListRoles",
"iam:ListUserPolicies",
"iam:ListUsers",
"iam:ListGroups",
"iam:ListGroupPolicies",
"kms:DescribeKey",
"kms:GetKeyPolicy",
"kms:GetKeyRotationStatus",
"kms:ListAliases",
"kms:ListKeys",
"kms:ListResourceTags",
"lambda:GetEventSourceMapping",
"lambda:GetFunction",
"lambda:GetFunctionCodeSigningConfig",
"lambda:ListEventSourceMappings",
"lambda:ListFunctions",
"lambda:ListVersionsByFunction",
"rds:DescribeDBInstances",
"rds:DescribeDBSubnetGroups",
"rds:ListTagsForResource",
"route53:GetHostedZone",
"route53:ListHostedZones",
"route53:ListResourceRecordSets",
"route53:ListTagsForResource",
"route53:ListHealthChecks",
"route53:GetHealthCheck",
"s3:GetAccelerateConfiguration",
"s3:GetAnalyticsConfiguration",
"s3:GetBucketAcl",
"s3:GetBucketCORS",
"s3:GetBucketLocation",
"s3:GetBucketLogging",
"s3:GetBucketNotification",
"s3:GetBucketObjectLockConfiguration",
"s3:GetBucketPolicy",
"s3:GetBucketRequestPayment",
"s3:GetBucketTagging",
"s3:GetBucketVersioning",
"s3:GetBucketWebsite",
"s3:GetEncryptionConfiguration",
"s3:GetInventoryConfiguration",
"s3:GetLifecycleConfiguration",
"s3:GetMetricsConfiguration",
"s3:GetReplicationConfiguration",
"s3:ListAllMyBuckets",
"s3:ListBucket",
"s3:GetBucketPublicAccessBlock",
"sqs:GetQueueAttributes",
"sqs:ListQueueTags",
"sqs:ListQueues",
"sns:ListTopics",
"sns:GetTopicAttributes",
"sns:ListTagsForResource",
"sns:ListSubscriptions",
"sns:ListSubscriptionsByTopic",
"sns:GetSubscriptionAttributes",
"dynamodb:ListTables",
"dynamodb:DescribeTable",
"dynamodb:DescribeGlobalTable",
"dynamodb:ListTagsOfResource",
"dynamodb:DescribeTimeToLive",
"dynamodb:DescribeTableReplicaAutoScaling",
"dynamodb:DescribeContinuousBackups",
"rds:DescribeDBClusters",
"rds:DescribeGlobalClusters",
"application-autoscaling:DescribeScalableTargets",
"application-autoscaling:DescribeScalingPolicies",
"application-autoscaling:DescribeScheduledActions",
"autoscaling:DescribeLaunchConfigurations",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeListeners"
]
}
]
}