Pike is a tool for determining the permissions or policy required for IAC code
9.2K
Pike is a tool to determine the minimum IAM permissions required to run OpenTofu/Terraform infrastructure code.
What's new?
Pike currently supports OpenTofu/Terraform and supports multiple providers (AWS, GCP and AZURE); Azure is the newest with AWS having the most supported resources https://github.com/JamesWoolfenden/pike/tree/master/src/mapping. Feel free to submit PR or Issue if you find an issue or even better add new resources, and then I'll take a look at merging it ASAP.
Note on Placeholder Resources: Some resources in the lookup maps (particularly GCP organization, folder, and billing-level resources) are marked with placeholder entries. These resources are recognized by Pike but do not have empirically validated IAM permissions because they require organization-level access or specialized account types that are difficult to test. When Pike encounters these resources, it will not error, but the permissions have not been validated through actual resource lifecycle testing.
CAVEAT The outputs of this tool are your first step, if you have AWS, you can now generate resources partially, there are no conditions and even partial resources are wild-carded (for now). (for AWS)minimum best practice would go further (and I am working on it as well), you will need to modify these permissions to the required in your environment by adding these restrictions, you can also deploy using short-lived credentials (using this tool or Vault) (in AWS so far), generating short-lived credentials for your build and then remotely (REMOTE) supply and invoke your builds (INVOKE).
Ideally I would like to do this for you, but these policies are currently determined statically (QUICKER), and unrecorded intentions can be impossible to infer.
Get started with Pike in 3 steps:
Install Pike
# macOS
brew tap jameswoolfenden/homebrew-tap
brew install jameswoolfenden/tap/pike
# Windows (using Scoop)
scoop bucket add iac https://github.com/JamesWoolfenden/scoop.git
scoop install pike
# Or install from source
go install github.com/jameswoolfenden/pike@latest
Scan your OpenTofu/Terraform code
pike scan -d ./path/to/your/terraform
This outputs the minimum IAM permissions required as JSON.
Generate as Terraform/OpenTofu code
pike scan -o terraform -d ./path/to/your/terraform
This creates an aws_iam_policy resource you can deploy.
Next steps: Use pike make to deploy the policy directly, or pike compare to validate against existing policies. See Usage for all commands.
Download the latest binary here:
https://github.com/JamesWoolfenden/pike/releases
Install from code:
go installInstall remotely:
go install github.com/jameswoolfenden/pike@latest
brew tap jameswoolfenden/homebrew-tap
brew install jameswoolfenden/tap/pike
I'm now using Scoop to distribute releases, it's much quicker to update and easier to manage than previous methods, you can install scoop from https://scoop.sh/.
Add my scoop bucket:
scoop bucket add iac https://github.com/JamesWoolfenden/scoop.git
Then you can install a tool:
scoop install pike
docker pull jameswoolfenden/pike
docker run --tty --volume /local/path/to/tf:/tf jameswoolfenden/pike scan -d /tf
https://hub.docker.com/repository/docker/jameswoolfenden/pike
To scan a directory containing OpenTofu/Terraform files:
./pike scan -d .\terraform\
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Action": [
"ec2:MonitorInstances",
"ec2:UnmonitorInstances",
"ec2:DescribeInstances",
"ec2:DescribeTags",
"ec2:DescribeInstanceAttribute",
"ec2:DescribeVolumes",
"ec2:DescribeInstanceTypes",
"ec2:RunInstances",
"ec2:DescribeInstanceCreditSpecifications",
"ec2:StopInstances",
"ec2:StartInstances",
"ec2:ModifyInstanceAttribute",
"ec2:TerminateInstances",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:CreateSecurityGroup",
"ec2:DescribeSecurityGroups",
"ec2:DescribeAccountAttributes",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteSecurityGroup",
"ec2:RevokeSecurityGroupEgress"
],
"Resource": "*"
}
}
You can also generate the policy as OpenTofu/Terraform instead:
$pike scan -o terraform -d ../modules/aws/terraform-aws-activemq
resource "aws_iam_policy" "terraformXVlBzgba" {
name = "terraformXVlBzgba"
path = "/"
description = "Add Description"
policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ec2:AuthorizeSecurityGroupEgress",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CreateNetworkInterface",
"ec2:CreateNetworkInterfacePermission",
"ec2:CreateSecurityGroup",
"ec2:CreateTags",
"ec2:DeleteNetworkInterface",
"ec2:DeleteNetworkInterfacePermission",
"ec2:DeleteSecurityGroup",
"ec2:DeleteTags",
"ec2:DescribeAccountAttributes",
"ec2:DescribeInternetGateways",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeVpcs",
"ec2:DetachNetworkInterface",
"ec2:RevokeSecurityGroupEgress",
"ec2:RevokeSecurityGroupIngress"
],
"Resource": "*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"kms:CreateKey",
"kms:DescribeKey",
"kms:EnableKeyRotation",
"kms:GetKeyPolicy",
"kms:GetKeyRotationStatus",
"kms:ListResourceTags",
"kms:ScheduleKeyDeletion",
"kms:TagResource",
"kms:UntagResource"
],
"Resource": "*"
},
{
"Sid": "VisualEditor2",
"Effect": "Allow",
"Action": [
"mq:CreateBroker",
"mq:CreateConfiguration",
"mq:CreateTags",
"mq:CreateUser",
"mq:DeleteBroker",
"mq:DeleteTags",
"mq:DeleteUser",
"mq:DescribeBroker",
"mq:DescribeConfiguration",
"mq:DescribeConfigurationRevision",
"mq:DescribeUser",
"mq:RebootBroker",
"mq:UpdateBroker",
"mq:UpdateConfiguration",
"mq:UpdateUser"
],
"Resource": "*"
}
]
})
}
And I am working on further enhancements to policy generation, if you have AWS auth installed:
e:\pike scan -d . -i -e
9:13AM DBG terraform init at E:\Code\modules\aws\terraform-aws-activemq
9:13AM DBG downloaded ip
resource "aws_iam_policy" "terraform_pike" {
name_prefix = "terraform_pike"
path = "/"
description = "Pike Autogenerated policy from IAC"
policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ec2:AuthorizeSecurityGroupEgress",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CreateNetworkInterface",
"ec2:CreateNetworkInterfacePermission",
"ec2:CreateSecurityGroup",
"ec2:CreateTags",
"ec2:DeleteNetworkInterface",
"ec2:DeleteNetworkInterfacePermission",
"ec2:DeleteSecurityGroup",
"ec2:DeleteTags",
"ec2:DescribeAccountAttributes",
"ec2:DescribeInternetGateways",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeVpcs",
"ec2:DetachNetworkInterface",
"ec2:RevokeSecurityGroupEgress",
"ec2:RevokeSecurityGroupIngress"
],
"Resource": [
"arn:aws:ec2:eu-west-2:680235478471:*"
]
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"kms:CreateGrant"
],
"Resource": [
"arn:aws:kms:eu-west-2:680235478471:*"
]
},
{
"Sid": "VisualEditor2",
"Effect": "Allow",
"Action": [
"mq:CreateBroker",
"mq:CreateConfiguration",
"mq:CreateTags",
"mq:CreateUser",
"mq:DeleteBroker",
"mq:DeleteTags",
"mq:DeleteUser",
"mq:DescribeBroker",
"mq:DescribeConfiguration",
"mq:DescribeConfigurationRevision",
"mq:DescribeUser",
"mq:RebootBroker",
"mq:UpdateBroker",
"mq:UpdateConfiguration",
"mq:UpdateUser"
],
"Resource": [
"arn:aws:mq:eu-west-2:680235478471:*"
]
}
]
})
}
When a Terraform config manages its own IAM (google_project_iam_*, aws_iam_role_policy*, azurerm_role_assignment), the minimum-sufficient role pike computes is also owner-equivalent — the holder can grant themselves anything. Pike always warns about these on stderr:
WARNING: escalation-class permissions detected — holder can grant themselves additional access.
Consider the two-role pattern: planner SA (read-only) on all branches,
applier SA (full) on protected branches only.
aws: iam:PassRole, iam:PutRolePolicy
No flag is needed to enable this; it fires whenever escalation-class permissions are present.
The same warning also fires for dangerous combinations of otherwise-unremarkable permissions — e.g. iam:PassRole paired with lambda:CreateFunction or ec2:RunInstances lets you choose which compute resource receives that role, and iam:CreateAccessKey paired with iam:ListUsers lets you mint credentials for any user:
Dangerous combinations (no single permission above reveals this; together they do):
aws: iam:PassRole + lambda:CreateFunction
Pass --strict to pike scan to exit non-zero when either kind of escalation-class permission is present, even though scan still succeeds in producing output (pike compare --strict does the same for the live-vs-IaC comparison). Pass --no-lint to suppress the warning (and the --strict check) entirely — useful if you're piping scan's policy output somewhere and don't want lint noise on stderr.
pike scan --strict -d ./terraform
pike scan --no-lint -o json -d ./terraform | some-other-tool
Use --output split (or -o split) to emit two permission sets — base (safe to distribute broadly) and escalation (owner-equivalent, restrict to protected branches) — instead of a single flat policy:
pike scan -o split -d ./terraform
{
"aws": {
"base": [
"firehose:CreateDeliveryStream",
"firehose:DeleteDeliveryStream",
"iam:GetRole",
"iam:ListAttachedRolePolicies"
],
"escalation": [
"iam:AttachRolePolicy",
"iam:PassRole",
"iam:PutRolePolicy"
]
}
}
The base set is the planner SA role (safe for terraform plan on every branch). The escalation set is the additional permissions the applier SA needs — bind this role only on protected branches.
With --write / -w, the split JSON is saved to .pike/pike.generated_policy.split.json.
Pike also warns when your Terraform manages stateful or expensive resources (GKE/EKS clusters, Cloud SQL/RDS databases, storage buckets, KMS keys) where accidental deletion is costly:
WARNING: deletion-sensitive permissions detected — granting destroy on stateful/expensive resources.
GCP:
[HIGH] google_container_cluster
GKE clusters take 10+ minutes to recreate; workloads experience downtime
→ deletion_protection = true
[HIGH] google_sql_database_instance
Cloud SQL instances contain stateful data; point-in-time recovery may be lost
→ deletion_protection_enabled = true
Use --sensitive to generate a three-role split that separates high-risk delete permissions into a break-glass role:
pike scan -d ./terraform --sensitive
This outputs three Terraform roles:
terraform_pike_safe — day-to-day operations (create, read, update) without high-risk deletesterraform_pike_delete — high-risk destruction permissions only (break-glass, requires approval)terraform_pike_plan — read-only permissions for terraform planGrant the delete role only to:
If you select the -w flag, pike will write out the role/policy required to build your project into the .pike folder:
$pike scan -w -i -d .
2022/09/17 13:50:51 terraform init at .
2022/09/17 13:50:51 downloaded ip
The .pike folder will contain:
aws_iam_role.terraform_pike.tf
pike.generated_policy.tf
Which you can deploy using OpenTofu/Terraform to create the role/policy to build your infrastructure project.
You can now deploy the policy you need directly (AWS only so far):
$pike make -d ../modules/aws/terraform-aws-apigateway/
2022/09/18 08:53:41 terraform init at ..\modules\aws\terraform-aws-apigateway\
2022/09/18 08:53:41 modules not found at ..\modules\aws\terraform-aws-apigateway\
2022/09/18 08:53:49 aws role create/updated arn:aws:iam::680235478471:role/terraform_pike_20220918071439382800000002
arn:aws:iam::680235478471:role/terraform_pike_20220918071439382800000002
This new verb returns the ARN of the role created, and you can find the Terraform used in your .pike folder.
Invoke is currently for triggering GitHub actions, if supplied with the workflow (defaults to main.yaml), repository and branch (defaults to main) flags, it will trigger the dispatch event.
You'll need to include the dispatch event in your workflow:
on:
workflow_dispatch:
push:
branches:
- master
To authenticate with the GitHub API, you will need to set your GitHub Personal Access Token, as the environment variable GITHUB_TOKEN
To Invoke a workflow, it is then:
pike invoke -workflow master.yml -branch master -repository JamesWoolfenden/terraform-aws-s3
I created Invoke to be used in tandem with the new remote command which supplies temporary credentials to a workflow.
**Note The GitHub API is rate-limited, usually 5000 calls per hour.
pike make -d ./module/aws/terraform-aws-s3/example/examplea
Apply is an extension to make and will apply the policy and role and use that role to create your infrastructure:
pike apply -d ./module/aws/terraform-aws-s3/example/examplea -region eu-west-2
It is intended for testing and developing the permissions for Pike itself
Remote uses the core code of make and apply, to write temporary AWS credentials(only so far) into your workflow.
pike remote -d ./module/aws/terraform-aws-s3/example/examplea -region eu-west-2 -repository terraform-aws-s3
Pike can now be used to update a projects README.md file:
./pike readme -o terraform -d ..\modules\aws\terraform-aws-activemq\
This looks in the README for the delimiters:
<!-- BEGINNING OF PRE-COMMIT-PIKE DOCS HOOK -->
<!-- END OF PRE-COMMIT-PIKE DOCS HOOK -->
and replaces is either with JSON or Terraform like so:
This is the policy required to build this project:
The Policy required is
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Action": [
"mq:CreateTags",
"mq:DeleteTags",
"ec2:DescribeInternetGateways",
"ec2:DescribeAccountAttributes",
"ec2:DescribeVpcs",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:CreateNetworkInterface",
"ec2:CreateNetworkInterfacePermission",
"ec2:DeleteNetworkInterfacePermission",
"ec2:DetachNetworkInterface",
"ec2:DeleteNetworkInterface",
"mq:CreateBroker",
"mq:DescribeBroker",
"mq:DescribeUser",
"mq:UpdateBroker",
"mq:DeleteBroker",
"mq:CreateConfiguration",
"mq:UpdateConfiguration",
"mq:DescribeConfiguration",
"mq:DescribeConfigurationRevision",
"mq:RebootBroker",
"ec2:CreateTags",
"ec2:DeleteTags",
"ec2:CreateSecurityGroup",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteSecurityGroup",
"ec2:RevokeSecurityGroupEgress",
"kms:TagResource",
"kms:UntagResource",
"kms:EnableKeyRotation",
"kms:CreateKey",
"kms:DescribeKey",
"kms:GetKeyPolicy",
"kms:GetKeyRotationStatus",
"kms:ListResourceTags",
"kms:ScheduleKeyDeletion"
],
"Resource": "*"
}
}
You can see an example here https://github.com/jamesWoolfenden/terraform-aws-activemq#policy.
Want to check your deployed IAM policy against your infrastructure code requirement?
This works for AWS and GCP.
$./pike compare -d ../modules/aws/terraform-aws-appsync -a arn:aws:iam::680235478471:policy/basic
IAM Policy arn:aws:iam::680235478471:policy/basic versus Infrastructure Code ../modules/aws/terraform-aws-appsync
{
"Statement": [
0: {
"Action": [
- 0: "kinesisvideo:CreateStream"
+ 0: "firehose:CreateDeliveryStream"
+ 0: "firehose:CreateDeliveryStream"
+ 1: "firehose:DeleteDeliveryStream"
+ 2: "firehose:DescribeDeliveryStream"
+ 3: "firehose:ListTagsForDeliveryStream"
+ 4: "iam:AttachRolePolicy"
+ 5: "iam:CreateRole"
+ 6: "iam:DeleteRole"
+ 7: "iam:DetachRolePolicy"
+ 8: "iam:GetRole"
+ 9: "iam:ListAttachedRolePolicies"
+ 10: "iam:ListInstanceProfilesForRole"
+ 11: "iam:ListRolePolicies"
+ 12: "iam:PassRole"
+ 13: "iam:TagRole"
+ 14: "kms:CreateKey"
+ 15: "kms:DescribeKey"
+ 16: "kms:EnableKeyRotation"
+ 17: "kms:GetKeyPolicy"
+ 18: "kms:GetKeyRotationStatus"
+ 19: "kms:ListResourceTags"
+ 20: "kms:ScheduleKeyDeletion"
+ 21: "logs:AssociateKmsKey"
+ 22: "logs:CreateLogGroup"
+ 23: "logs:DeleteLogGroup"
+ 24: "logs:DeleteRetentionPolicy"
+ 25: "logs:DescribeLogGroups"
+ 26: "logs:DisassociateKmsKey"
+ 27: "logs:ListTagsLogGroup"
+ 28: "logs:PutRetentionPolicy"
+ 29: "s3:CreateBucket"
+ 30: "s3:DeleteBucket"
+ 31: "s3:GetAccelerateConfiguration"
+ 32: "s3:GetBucketAcl"
+ 33: "s3:GetBucketCORS"
+ 34: "s3:GetBucketLogging"
+ 35: "s3:GetBucketObjectLockConfiguration"
+ 36: "s3:GetBucketPolicy"
+ 37: "s3:GetBucketPublicAccessBlock"
+ 38: "s3:GetBucketRequestPayment"
+ 39: "s3:GetBucketTagging"
+ 40: "s3:GetBucketVersioning"
+ 41: "s3:GetBucketWebsite"
+ 42: "s3:GetEncryptionConfiguration"
+ 43: "s3:GetLifecycleConfiguration"
+ 44: "s3:GetObject"
+ 45: "s3:GetObjectAcl"
+ 46: "s3:GetReplicationConfiguration"
+ 47: "s3:ListAllMyBuckets"
+ 48: "s3:ListBucket"
+ 49: "s3:PutBucketAcl"
+ 50: "s3:PutBucketPublicAccessBlock"
+ 51: "s3:PutEncryptionConfiguration"
+ 52: "wafv2:CreateWebACL"
+ 53: "wafv2:DeleteWebACL"
+ 54: "wafv2:GetWebACL"
],
"Effect": "Allow",
"Resource": "*",
- "Sid": ""
+ "Sid": "VisualEditor0"
}
],
"Version": "2012-10-17"
}
Pike always warns to stderr when the computed policy contains escalation-class permissions (see Escalation-class permissions). Pass --strict to also exit non-zero in that case, even if the policies otherwise match — useful in CI to enforce the two-role pattern:
pike compare --strict -d ./terraform -a arn:aws:iam::680235478471:policy/terraform_pike
--target-file reads the baseline policy from a local file instead of calling the live AWS/GCP API, so CI can diff against a committed snapshot with no cloud credentials at all. --write-baseline writes the freshly generated IaC policy to a file, to seed or refresh that snapshot. -a/--arn is still required (its format is how pike picks AWS vs GCP), but with --target-file set it's never used to make a live call.
# One-off, with live credentials: seed the baseline and confirm it matches deployed today.
pike compare -d ./terraform -a arn:aws:iam::680235478471:policy/terraform_pike --write-baseline ./policy-baseline.json
# Every CI run after that: no credentials needed.
pike compare -d ./terraform -a arn:aws:iam::680235478471:policy/terraform_pike --target-file ./policy-baseline.json
pike audit runs a set of deterministic rules over the IAM-relevant resources in your Terraform — wildcard actions, escalation-class permissions granted without a scoping Condition, public principals, admin-class managed policy attachments, risky trust-policy principals, and more — without needing any cloud credentials at all.
pike audit -d ./terraform
Exits non-zero when
Content type
Image
Digest
sha256:35fdb89dd…
Size
21.4 MB
Last updated
3 days ago
docker pull jameswoolfenden/pike