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:GetRepositoryPolicy"
"ecr:ListTagsForResource",
"elasticache:DescribeCacheClusters",
"iam:GetPolicy",
"iam:GetPolicyVersion",
"iam:GetRole",
"iam:GetRolePolicy",
"iam:GetUser",
"iam:GetUserPolicy",
"iam:ListAccessKeys",
"iam:ListAttachedRolePolicies",
"iam:ListAttachedUserPolicies",
"iam:ListAttachedGroupPolicies",
"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.
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:
- 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
- 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:GetRepositoryPolicy"
"ecr:ListTagsForResource",
"elasticache:DescribeCacheClusters",
"iam:GetPolicy",
"iam:GetPolicyVersion",
"iam:GetRole",
"iam:GetRolePolicy",
"iam:GetUser",
"iam:GetUserPolicy",
"iam:ListAccessKeys",
"iam:ListAttachedRolePolicies",
"iam:ListAttachedUserPolicies",
"iam:ListAttachedGroupPolicies",
"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"
]
}
]
}