IAM Users and Policies
Create IAM users, manage access keys, and control permissions with policies.
Overview
Spinifex implements AWS-compatible IAM covering user management, access key lifecycle, policy CRUD, and policy attachment. Inline user policies (put-user-policy), user and policy tagging, groups, and roles and instance profiles are also supported. All IAM resources are scoped to the account that creates them — users in one account cannot see or modify resources in another.
When you create an account with spx admin account create, Spinifex bootstraps a root user with an AdministratorAccess policy and writes the credentials to ~/.aws/credentials. From there, you use the standard AWS CLI to manage additional users and permissions.
How authentication works: Every AWS CLI request is signed with SigV4 using an access key pair. The gateway verifies the signature, resolves the caller's account, and evaluates attached policies before routing the request. The root user (account 000000000000) bypasses policy evaluation entirely.
Prerequisites
- A running Spinifex cluster (see Setting Up Your Cluster)
- AWS CLI configured with the
spinifexprofile:
export AWS_PROFILE=spinifex
Instructions
Users
Create a User
aws iam create-user --user-name alice
The response includes the user's ARN, unique ID, and creation date:
{
"User": {
"UserName": "alice",
"UserId": "AIDA1A2B3C4D5E6F7890",
"Arn": "arn:aws:iam::000000000001:user/alice",
"Path": "/",
"CreateDate": "2026-03-24T10:00:00Z"
}
}
Use --path to organise users into hierarchical groups:
aws iam create-user --user-name bob --path /developers/
Get a User
aws iam get-user --user-name alice
List Users
aws iam list-users
Filter by path prefix:
aws iam list-users --path-prefix /developers/
Delete a User
Before deleting a user, remove all access keys and detach all policies:
# Remove access keys
aws iam list-access-keys --user-name alice
aws iam delete-access-key --user-name alice --access-key-id AKIA...
# Detach policies
aws iam list-attached-user-policies --user-name alice
aws iam detach-user-policy --user-name alice \
--policy-arn arn:aws:iam::000000000001:policy/MyPolicy
# Now delete
aws iam delete-user --user-name alice
Access Keys
Access keys are how users authenticate with the AWS CLI. Each user can have up to 2 access keys at a time, allowing key rotation without downtime.
Create an Access Key
aws iam create-access-key --user-name alice
{
"AccessKey": {
"UserName": "alice",
"AccessKeyId": "AKIA1A2B3C4D5E6F7890ABCD",
"Status": "Active",
"SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"CreateDate": "2026-03-24T10:05:00Z"
}
}
The secret access key is only shown once. Save it immediately. If lost, delete the key and create a new one.
Configure a profile for the new key. A Spinifex profile also needs the gateway endpoint and CA bundle (copy the values from your existing spinifex-* profile in ~/.aws/config):
aws configure set aws_access_key_id AKIA1A2B3C4D5E6F7890ABCD --profile spinifex-alice
aws configure set aws_secret_access_key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY --profile spinifex-alice
aws configure set region ap-southeast-2 --profile spinifex-alice
aws configure set endpoint_url https://localhost:9999 --profile spinifex-alice
aws configure set ca_bundle /var/lib/spinifex/config/ca.pem --profile spinifex-alice
Then set AWS_PROFILE=spinifex-alice to use it.
List Access Keys
aws iam list-access-keys --user-name alice
Rotate Access Keys
Create a second key, update your configuration, then delete the old one:
# 1. Create new key (while old key still works)
aws iam create-access-key --user-name alice
# 2. Update the profile with the new key
aws configure set aws_access_key_id AKIA_NEW_KEY_ID --profile spinifex-alice
aws configure set aws_secret_access_key NEW_SECRET --profile spinifex-alice
# 3. Verify the new key works
AWS_PROFILE=spinifex-alice aws sts get-caller-identity
# 4. Delete the old key
aws iam delete-access-key --user-name alice --access-key-id AKIA_OLD_KEY_ID
Deactivate an Access Key
Temporarily disable a key without deleting it:
aws iam update-access-key --user-name alice \
--access-key-id AKIA1A2B3C4D5E6F7890ABCD \
--status Inactive
Reactivate it later:
aws iam update-access-key --user-name alice \
--access-key-id AKIA1A2B3C4D5E6F7890ABCD \
--status Active
Policies
Policies are JSON documents that define what actions a user can perform on which resources.
Policy Document Format
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["ec2:RunInstances", "ec2:DescribeInstances"],
"Resource": "*"
}
]
}
Each statement has:
- Effect —
AlloworDeny - Action — Service actions (e.g.
ec2:RunInstances,s3:GetObject). Supports wildcards:ec2:*,s3:Get*, or*for all actions. - Resource — Target resources. Use
*for all resources.
Evaluation order: An explicit Deny always wins. If no statement matches, access is denied by default.
Create a Policy
Save the policy document to a file, then create the policy:
cat > /tmp/ec2-readonly.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:DescribeImages",
"ec2:DescribeKeyPairs",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeVpcs"
],
"Resource": "*"
}
]
}
EOF
aws iam create-policy \
--policy-name EC2ReadOnly \
--policy-document file:///tmp/ec2-readonly.json
{
"Policy": {
"PolicyName": "EC2ReadOnly",
"PolicyId": "ANPA1A2B3C4D5E6F7890",
"Arn": "arn:aws:iam::000000000001:policy/EC2ReadOnly",
"Path": "/",
"DefaultVersionId": "v1",
"CreateDate": "2026-03-24T10:10:00Z"
}
}
Common Policy Examples
Full administrator access:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
]
}
S3 read/write (for hybrid sync, backups):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket",
"s3:DeleteObject"
],
"Resource": "*"
}
]
}
EC2 operator (launch and manage instances, no VPC changes):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:RunInstances",
"ec2:StartInstances",
"ec2:StopInstances",
"ec2:TerminateInstances",
"ec2:DescribeInstances",
"ec2:DescribeImages"
],
"Resource": "*"
}
]
}
Deny terminate (attach alongside a broader Allow to prevent accidental termination):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": "ec2:TerminateInstances",
"Resource": "*"
}
]
}
Get a Policy
aws iam get-policy \
--policy-arn arn:aws:iam::000000000001:policy/EC2ReadOnly
Get the Policy Document
Use get-policy-version with version v1 to retrieve the actual JSON document:
aws iam get-policy-version \
--policy-arn arn:aws:iam::000000000001:policy/EC2ReadOnly \
--version-id v1
List Policies
aws iam list-policies
Delete a Policy
A policy must be detached from all users before it can be deleted:
aws iam delete-policy \
--policy-arn arn:aws:iam::000000000001:policy/EC2ReadOnly
Attaching Policies
Policies have no effect until attached to a user.
Attach a Policy to a User
aws iam attach-user-policy --user-name alice \
--policy-arn arn:aws:iam::000000000001:policy/EC2ReadOnly
List a User's Policies
aws iam list-attached-user-policies --user-name alice
Detach a Policy
aws iam detach-user-policy --user-name alice \
--policy-arn arn:aws:iam::000000000001:policy/EC2ReadOnly
Putting It All Together
A complete workflow for onboarding a developer with scoped EC2 and S3 access:
# 1. Create the user
aws iam create-user --user-name dev-carol --path /developers/
# 2. Create an access key
aws iam create-access-key --user-name dev-carol
# Save the AccessKeyId and SecretAccessKey from the output
# 3. Create a scoped policy
cat > /tmp/dev-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EC2Access",
"Effect": "Allow",
"Action": ["ec2:*"],
"Resource": "*"
},
{
"Sid": "S3ReadOnly",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": "*"
},
{
"Sid": "NoTerminate",
"Effect": "Deny",
"Action": "ec2:TerminateInstances",
"Resource": "*"
}
]
}
EOF
aws iam create-policy \
--policy-name DeveloperAccess \
--policy-document file:///tmp/dev-policy.json
# 4. Attach the policy
aws iam attach-user-policy --user-name dev-carol \
--policy-arn arn:aws:iam::000000000001:policy/DeveloperAccess
# 5. Configure the developer's AWS CLI profile with the key from step 2.
# endpoint_url and ca_bundle: copy from an existing spinifex-* profile in ~/.aws/config
aws configure set aws_access_key_id AKIA_CAROL_KEY_ID --profile spinifex-carol
aws configure set aws_secret_access_key CAROL_SECRET --profile spinifex-carol
aws configure set region ap-southeast-2 --profile spinifex-carol
aws configure set endpoint_url https://localhost:9999 --profile spinifex-carol
aws configure set ca_bundle /var/lib/spinifex/config/ca.pem --profile spinifex-carol
# 6. Verify
AWS_PROFILE=spinifex-carol aws ec2 describe-instances
AWS_PROFILE=spinifex-carol aws ec2 terminate-instances --instance-ids i-123
# ^ This will be denied by the NoTerminate statement
Troubleshooting
AccessDenied on IAM Commands
The calling user must have IAM permissions. Attach a policy with iam:* actions, or use the account's admin profile:
export AWS_PROFILE=spinifex
aws iam list-users
InvalidClientTokenId
The access key is either inactive or does not exist. Check the key status:
aws iam list-access-keys --user-name alice
If the key shows Inactive, reactivate it:
aws iam update-access-key --user-name alice \
--access-key-id AKIA... --status Active
If the key was deleted, create a new one.
DeleteConflict When Deleting a User
The user still has access keys, attached policies, or group memberships. Remove them first:
# Check for access keys
aws iam list-access-keys --user-name alice
# Check for attached policies
aws iam list-attached-user-policies --user-name alice
# Check for group memberships
aws iam list-groups-for-user --user-name alice
DeleteConflict When Deleting a Policy
The policy is still attached to one or more users. Detach it from all users before deleting:
# Find who has it attached, then detach
aws iam detach-user-policy --user-name alice \
--policy-arn arn:aws:iam::000000000001:policy/MyPolicy
aws iam delete-policy \
--policy-arn arn:aws:iam::000000000001:policy/MyPolicy
LimitExceeded When Creating Access Keys
Each user can have at most 2 access keys. Delete an existing key before creating a new one:
aws iam list-access-keys --user-name alice
aws iam delete-access-key --user-name alice --access-key-id AKIA_OLD
aws iam create-access-key --user-name alice
MalformedPolicyDocument
The policy JSON is invalid. Check that:
Versionis exactly"2012-10-17"- At least one
Statementexists - Each statement has
Effect(Allow/Deny),Action, andResource - The document is under 6144 bytes
- The JSON is well-formed (no trailing commas, correct quoting)