Expand AWS IAM Actions with Wildcards
npm install @cloud-copilot/iam-expand   
Built in the Unix philosophy, this is a small tool that does one thing well: explain IAM actions with wildcards.
Use this to:
1. Expand wildcards when you are not allowed to use them in your policies.
2. Get an exhaustive list of actions that are included in a policy to quickly search it for interesting actions.
3. Investigate where interesting or dubious actions are being used in your policies.
!Demo
Extended demo on YouTube.
Published as an npm package in ESM and CommonJS plus available as a CLI.
All information is sourced from @cloud-copilot/iam-data which is updated daily.
iam-expand intends to only return valid, actual actions, if any invalid values are passed in such as an invalid format or a service/action that does not exist, they will be left out of the output. There are options to override this behavior.
http://iam.cloudcopilot.io/tools/iam-expand
There is a CLI! The examples folder has examples showing how to use the CLI to find interesting actions in your IAM policies.
You can install it globally. This also works in the default AWS CloudShell!
``bash`
npm install -g @cloud-copilot/iam-expand
_Depending on your configuration sudo may be required to install globally._
You can also install the CLI in a project and run it with npx.
`bash`
npm install @cloud-copilot/iam-expandRun with npx inside your project
npx @cloud-copilot/iam-expand
The simplest usage is to pass in the actions you want to expand.
`bash`
iam-expand s3:Get*TaggingOutputs all Get*Tagging actions
s3:GetBucketTagging
s3:GetJobTagging
s3:GetObjectTagging
s3:GetObjectVersionTagging
s3:GetStorageLensConfigurationTaggin
`bash`
iam-expand s3:GetTagging s3:PutTaggingOutputs the combination of GetTagging and PutTagging actions deduplicated and sorted
s3:GetBucketTagging
s3:GetJobTagging
s3:GetObjectTagging
s3:GetObjectVersionTagging
s3:GetStorageLensConfigurationTagging
s3:PutBucketTagging
s3:PutJobTagging
s3:PutObjectTagging
s3:PutObjectVersionTagging
s3:PutStorageLensConfigurationTaggin
Use this to find all actions that are not in a set of patterns
`bash`
iam-expand --invert s3:GetTagging s3:PutTagging
#Outputs all actions that are not GetTagging or PutTagging
a2c:GetContainerizationJobDetails
a2c:GetDeploymentJobDetails
a2c:StartContainerizationJob
a2c:StartDeploymentJob
a4b:ApproveSkill
a4b:AssociateContactWithAddressBook
...
Run the command with no options to show usage:
`bash`
iam-expand
#### --expand-asterisk
By default, a single will not be expanded. If you want to expand a single you can set this flag.
`bash
iam-expand "*"Returns the asterisk
*
iam-expand --expand-asterisk "*"
####
--error-on-invalid-formatBy default, if an invalid format is passed in, such as:
-
s3Get*Tagging (missing a separator) or
- s3:Get:Tagging* (too many separators)it will be silenty ignored and left out of the output. If you want to throw an error when an invalid format is passed in you can set this flag.
`bash
iam-expand "s3Get*Tagging"
Returns nothing
iam-expand --error-on-invalid-format "s3Get*Tagging"
Throws an error and returns a non zero exit code
Invalid action format: s3Get*Tagging
`####
--error-on-invalid-serviceBy default, if a service is passed in that does not exist in the IAM data, it will be silently ignored and left out of the output. If you want to throw an error when a service is passed in that does not exist you can set this flag.
`bash
iam-expand "r2:Get*Tagging"
Returns nothing
iam-expand --error-on-invalid-service "r2:Get*Tagging"
Throws an error and returns a non zero exit code
Service not found: r2
`####
--invalid-action-behaviorBy default, if an action is passed in that does not exist in the IAM data, it will be silently ignored and left out of the output. There are two options to override this behavior:
error and include.`bash
iam-expand "ec2:DestroyAvailabilityZone"
Returns nothing
iam-expand --invalid-action-behavior remove "ec2:DestroyAvailabilityZone"
Returns nothing
iam-expand --invalid-action-behavior error "ec2:DestroyAvailabilityZone"
Throws an error and returns a non zero exit code
Invalid action: ec2:DestroyAvailabilityZone
iam-expand --invalid-action-behavior include "ec2:DestroyAvailabilityZone"
Returns the invalid action
ec2:DestroyAvailabilityZone
`####
--invertUse this to find all actions that are not in a set of patterns. Only works for actions passed as arguments, or unstructured content from stdin.
`bash
iam-expand --invert s3:GetTagging s3:PutTagging
#Outputs all actions that are not GetTagging or PutTagging
a2c:GetContainerizationJobDetails
a2c:GetDeploymentJobDetails
a2c:StartContainerizationJob
a2c:StartDeploymentJob
a4b:ApproveSkill
a4b:AssociateContactWithAddressBook
...
`####
--invert-not-actions_This operates only on JSON input_. It will recursively search the JSON document for any
NotAction that is a string or an array of strings. The NotAction will be replaced with an Action key that is the inverse of the NotAction actions or patterns.`bash
cat policy.json | iam-expand --invert-not-actions
`See Read from Stdin for more details
####
--show-data-versionShow the version of the data that is being used to expand the actions and exit.
`bash
iam-expand --show-data-version
@cloud-copilot/iam-data version: 0.3.202409051
Data last updated: Thu Sep 05 2024 04:46:39 GMT+0000 (Coordinated Universal Time)
Update with either:
npm update @cloud-copilot/iam-data
npm update -g @cloud-copilot/iam-data
`####
--read-wait-msWhen reading from stdin (see below) the CLI will wait 10 seconds for the first byte to be read before timing out. This is enough time for most operations. If you want to wait longer you can set this flag to the number of milliseconds you want to wait.
`bash
cat policy.json | iam-expand
Will wait up to 10 seconds for input to start, which is plenty of time for a local file.
curl "https://government-secrets.s3.amazonaws.com/secret-policy.json" | iam-expand --read-wait-ms 20000
Will wait up to 20 seconds to receive first byte from curl before timing out. Adjust as needed
`$3
If no actions are passed as arguments, the CLI will read from stdin.
#### Expanding JSON input
If the input is a valid json document, the CLI will find every instance of
Action and NotAction that is a string or an array of strings and expand them. This is useful for finding all the actions in a policy document or set of documents.Given
policy.json`json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:Get*Tagging",
"Resource": "*"
},
{
"Effect": "Deny",
"NotAction": ["s3:GetTagging", "s3:PutTagging"],
"Resource": "*"
}
]
}
``bash
cat policy.json | iam-expand > expanded-policy.json
`Gives this file in
expanded-policy.json`json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
// Was "s3:Get*Tagging"
"Action": [
"s3:GetBucketTagging",
"s3:GetJobTagging",
"s3:GetObjectTagging",
"s3:GetObjectVersionTagging",
"s3:GetStorageLensConfigurationTagging"
],
"Resource": "*"
},
{
"Effect": "Deny",
// Was ["s3:GetTagging", "s3:PutTagging"]
"NotAction": [
"s3:GetBucketTagging",
"s3:GetJobTagging",
"s3:GetObjectTagging",
"s3:GetObjectVersionTagging",
"s3:GetStorageLensConfigurationTagging",
"s3:PutBucketTagging",
"s3:PutJobTagging",
"s3:PutObjectTagging",
"s3:PutObjectVersionTagging",
"s3:PutStorageLensConfigurationTagging"
],
"Resource": "*"
}
]
}
`You can also invert the
NotAction using --invert-not-actions. This will replace the NotAction element with an Action element that is the inverse of actions listed in the NotAction.`bash
cat policy.json | iam-expand --invert-not-actions > inverted-policy.json
`Gives this file in
inverted-policy.json`json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
// Was "s3:Get*Tagging"
"Action": [
"s3:GetBucketTagging",
"s3:GetJobTagging",
"s3:GetObjectTagging",
"s3:GetObjectVersionTagging",
"s3:GetStorageLensConfigurationTagging"
],
"Resource": "*"
},
{
"Effect": "Deny",
// Was NotAction: ["s3:GetTagging", "s3:PutTagging"]
// Now is Action: everything but the GetTagging and PutTagging actions
"Action": [
"a2c:GetContainerizationJobDetails",
"a2c:GetDeploymentJobDetails",
"a2c:StartContainerizationJob",
...
"xray:UntagResource",
"xray:UpdateGroup",
"xray:UpdateSamplingRule",
],
"Resource": "*"
}
]
}
`You can also use this to expand the actions from the output of commands.
`bash
aws iam get-account-authorization-details --output json | iam-expand --read-wait-ms 20000 > expanded-authorization-details.json
Now you can search the output for actions you are interested in
grep -n "kms:DisableKey" expanded-authorization-details.json
`#### Expanding arbitrary input
If the input from stdin is not json, the content is searched for IAM actions then expands them. Throw anything at it and it will find all the actions it can and expand them.
You can echo content:
`bash
echo "s3:Get*Tagging" | iam-expand
`You can pull out part of a json file and pipe it in:
`bash
cat policy.json | jq '.Statement[].Action' | iam-expand
`Or some Terraform:
`bash
cat main.tf | iam-expand
`Or some CloudFormation:
`bash
cat template.yaml | iam-expand
`Or even some HTML:
`bash
curl "https://docs.aws.amazon.com/aws-managed-policy/latest/reference/ReadOnlyAccess.html" | iam-expand
`Or the output of any command.
Because of the likelyhood of finding an aseterik
in the input; if the value to stdin is not a valid json document the stdin option will not find or expand a single even if --expand-asterisk is passed.Please give this anything you can think of and open an issue if you see an opportunity for improvement.
Typescript/NodeJS Usage
Add to a project
`bash
npm install @cloud-copilot/iam-expand
``typescript
import { expandIamActions } from '@cloud-copilot/iam-expand'expandIamActions('s3:Get*Tagging')[
('s3:GetBucketTagging',
's3:GetJobTagging',
's3:GetObjectTagging',
's3:GetObjectVersionTagging',
's3:GetStorageLensConfigurationTagging')
]
expandIamActions(['s3:GetTagging', 's3:PutTagging'])[
('s3:GetBucketTagging',
's3:GetJobTagging',
's3:GetObjectTagging',
's3:GetObjectVersionTagging',
's3:GetStorageLensConfigurationTagging',
's3:PutBucketTagging',
's3:PutJobTagging',
's3:PutObjectTagging',
's3:PutObjectVersionTagging',
's3:PutStorageLensConfigurationTagging')
]
`API Reference
expandIamActionsexpandIamActions(actionStringOrStrings: string | string[], overrideOptions?: Partial is the main function that will expand the actions of the IAM policy. Takes a string or array of strings and returns an array of strings that the input matches.Only Valid Values
expandIamActions intends to only return valid actual actions, if any invalid values are passed in such as an invalid format or a service/action that does not exist, they will be left out of the output. There are options to override this behavior.Any escaped unicode characters will be converted to their original character as part of the process. So
s3:\\u0067et will be converted to s3:Get before processing. Even something like s3:\\u0067etBucket will be converted to s3:GetBucket even though it has no wildcards..Options
expandIamActions an optional second argument that is an object with the following options:$3
By default, a single
will not be expanded. If you want to expand a single you can set this option to true.`typescript
import { expandIamActions } from '@cloud-copilot/iam-expand';//Returns the unexpanded value
expandIamActions('*')
['*']
//Returns the expanded value
expandIamActions('*', { expandAsterisk: true })
[
//Many many strings. 🫢
]
`$3
By default, if an invalid format is passed in, such as:
-
s3Get*Tagging (missing a separator) or
- s3:Get:Tagging* (too many separators)it will be silenty ignored and left out of the output. If you want to throw an error when an invalid format is passed in you can set this option to
true.`typescript
import { expandIamActions } from '@cloud-copilot/iam-expand';//Ignore invalid format
expandIamActions('s3Get*Tagging')
[]
//Throw an error on invalid format
expandIamActions('s3Get*Tagging', { errorOnInvalidFormat: true })
//Uncaught Error: Invalid action format: s3Get*Tagging
`$3
By default, if a service is passed in that does not exist in the IAM data, it will be silently ignored and left out of the output. If you want to throw an error when a service is passed in that does not exist you can set this option to
true.`typescript
import { expandIamActions } from '@cloud-copilot/iam-expand';//Ignore invalid service
expandIamActions('r2:Get*Tagging')
[]
//Throw an error on invalid service
expandIamActions('r2:Get*Tagging', { errorOnInvalidService: true })
//Uncaught Error: Service not found: r2
`$3
By default, if an action is passed in that does not exist in the IAM data, it will be silently ignored and left out of the output. There are two options to override this behavior:
Error and Include.`typescript
import { expandIamActions, InvalidActionBehavior } from '@cloud-copilot/iam-expand';//Ignore invalid action by default
expandIamActions('ec2:DestroyAvailabilityZone')
[]
//Ignore invalid action explicitly
expandIamActions('ec2:DestroyAvailabilityZone', { invalidActionBehavior: InvalidActionBehavior.Remove })
[]
//Throw an error on invalid action
expandIamActions('ec2:DestroyAvailabilityZone', { invalidActionBehavior: InvalidActionBehavior.Error })
//Uncaught Error: Invalid action: ec2:DestroyAvailabilityZone
//Include invalid action
expandIamActions('ec2:DestroyAvailabilityZone', { invalidActionBehavior: InvalidActionBehavior.Include })
['ec2:DestroyAvailabilityZone']
`invertIamActionsinvertIamActions(actionString: string): string will take an action string and return all actions not matching . For example s3:GetTagging will return all actions from all services except those s3 actions that match the pattern GetTagging`.