Setup guide
Everything you must provision before this skill can run โ the exact credentials, permissions, and where to click. Links go to the official consoles and docs, which stay current.
An AWS account with AWS CLI v2 installed and a profile or role whose credentials can read and modify S3, EBS, EFS, RDS and DynamoDB in one known target region.
You need to already have
- An AWS account you are authorized to modify, and its 12-digit account id written down so the skill can refuse to run elsewhere
- AWS CLI v2 installed (aws --version must report aws-cli/2.x); v1 lacks --checksum-algorithm and several s3api options used here
- Python 3.11+ with boto3 >= 1.34 if you want the scripted paths
- jq installed for the reference bash script
- Permission to create or attach an IAM policy, or an administrator who will attach the policy for you
- For encrypted resources: the ARN of a customer-managed KMS key whose key policy trusts the principal this skill runs as
How the pieces connect
Credentials this skill needs
Set these as environment variables. Never paste secrets into a chat or commit them.
| Variable | What it is | Where to get it |
|---|---|---|
AWS_PROFILEoptional | Named AWS CLI profile (IAM Identity Center / SSO preferred) format: profile name from ~/.aws/config, e.g. storage-admin | Create with 'aws configure sso --profile storage-admin' (Identity Center) or 'aws configure --profile storage-admin' (static keys). Verify with 'aws configure list-profiles'. |
AWS_REGION | Target AWS region format: region code, e.g. us-east-2 or eu-west-1 | Read the region from the AWS console URL or the region selector in the top-right of the console; set it with 'aws configure set region us-east-2 --profile storage-admin'. |
AWS_EXPECTED_ACCOUNT_ID | Account id the skill is allowed to modify (safety guard) format: 12 digits, e.g. 123456789012 | Run 'aws sts get-caller-identity --query Account --output text', or read it from the account menu in the top-right of the AWS console. |
AWS_ACCESS_KEY_IDsecretoptional | Access key id (only for static long-lived credentials) format: 20-character string starting with AKIA (IAM user) or ASIA (temporary) | IAM console > Users > <user> > Security credentials > Create access key. Prefer IAM Identity Center or an assumed role instead of static keys. |
AWS_SECRET_ACCESS_KEYsecretoptional | Secret access key format: 40-character base64-like string, shown only once at creation | Shown together with the access key id at IAM console > Users > <user> > Security credentials > Create access key; it cannot be retrieved later. |
AWS_SESSION_TOKENsecretoptional | Session token (required with ASIA temporary credentials) format: long opaque string returned by sts:AssumeRole or SSO login | Returned by 'aws sts assume-role --role-arn arn:aws:iam::123456789012:role/StorageAdmin --role-session-name storage-skill', or managed automatically by 'aws sso login'. |
Permissions to grant
sts:GetCallerIdentityIAM policyPrints the account id and caller ARN before any mutation so the skill can refuse to run in the wrong account.
โณ Always allowed by default for any authenticated principal; cannot be narrowed further.
iam:SimulatePrincipalPolicyIAM policyRehearses whether the caller is allowed to perform a mutating action before attempting it, without side effects.
โณ Restrict Resource to the caller's own ARN so the principal cannot simulate other roles.
s3:ListAllMyBuckets, s3:GetBucketLocation, s3:GetBucketVersioning, s3:GetEncryptionConfiguration, s3:GetLifecycleConfiguration, s3:GetBucketPublicAccessBlockIAM policyInventories bucket configuration and reads existing lifecycle rules before replacing them.
โณ s3:ListAllMyBuckets requires Resource "*"; scope the remaining Get* actions to the specific bucket ARNs.
s3:PutBucketVersioning, s3:PutEncryptionConfiguration, s3:PutLifecycleConfiguration, s3:PutBucketPublicAccessBlockIAM policyApplies bucket hardening: versioning, default SSE-KMS, lifecycle transitions and Block Public Access.
โณ Scope Resource to arn:aws:s3:::<bucket> for the specific buckets in scope; omit entirely in audit-only mode.
s3:ListBucket, s3:ListBucketVersions, s3:GetObject, s3:GetObjectAttributes, s3:PutObjectIAM policyCopies objects during migration and verifies object size and SHA-256 checksums afterwards.
โณ Grant Get* only on the source bucket/prefix and Put* only on the destination bucket/prefix.
ec2:DescribeVolumes, ec2:DescribeSnapshots, ec2:DescribeVolumesModificationsIAM policyInventories EBS volumes and tracks snapshot and modification progress via waiters.
โณ EC2 Describe* actions do not support resource-level permissions; they require Resource "*".
ec2:CreateSnapshot, ec2:CopySnapshot, ec2:CreateVolume, ec2:ModifyVolume, ec2:CreateTagsIAM policyTakes pre-change EBS snapshots, copies them cross-region/encrypted, restores volumes and resizes or retypes volumes.
โณ Constrain with a Condition on aws:ResourceTag or ec2:CreateAction; scope ec2:CreateTags to snapshot and volume creation only.
elasticfilesystem:DescribeFileSystems, elasticfilesystem:DescribeLifecycleConfiguration, elasticfilesystem:PutLifecycleConfiguration, elasticfilesystem:DescribeBackupPolicy, elasticfilesystem:PutBackupPolicyIAM policyReads and sets EFS IA/Archive tiering policies and enables automatic EFS backups.
โณ Scope Resource to arn:aws:elasticfilesystem:<region>:<account>:file-system/fs-xxxx.
rds:DescribeDBInstances, rds:DescribeDBSnapshots, rds:CreateDBSnapshot, rds:CopyDBSnapshot, rds:RestoreDBInstanceFromDBSnapshot, rds:RestoreDBInstanceToPointInTime, rds:ModifyDBInstance, rds:AddTagsToResourceIAM policyCreates manual RDS snapshots, restores snapshots or point-in-time copies to new identifiers, and adjusts backup retention.
โณ Scope to arn:aws:rds:<region>:<account>:db:<identifier> and snapshot ARNs; deliberately exclude rds:DeleteDBInstance.
dynamodb:DescribeTable, dynamodb:DescribeContinuousBackups, dynamodb:UpdateContinuousBackups, dynamodb:CreateBackup, dynamodb:RestoreTableFromBackup, dynamodb:RestoreTableToPointInTime, dynamodb:ExportTableToPointInTime, dynamodb:DescribeExportIAM policyEnables PITR, takes on-demand backups, exports tables to S3 and restores tables under new names.
โณ Scope to arn:aws:dynamodb:<region>:<account>:table/<name>; exclude dynamodb:DeleteTable and dynamodb:DeleteBackup.
kms:DescribeKey, kms:Encrypt, kms:Decrypt, kms:GenerateDataKey, kms:CreateGrantIAM policyRequired for SSE-KMS buckets, encrypted EBS snapshot copies, and encrypted RDS snapshot copies across regions or accounts.
โณ Scope to the specific key ARN and add Condition kms:ViaService (for example s3.us-east-2.amazonaws.com) so the key is usable only through the intended service.
backup:StartBackupJob, backup:StartRestoreJob, backup:ListRecoveryPointsByBackupVault, iam:PassRoleIAM policyOptional path for centralized backup and restore through AWS Backup vaults instead of per-service snapshot APIs.
โณ Scope iam:PassRole to the single AWSBackupDefaultServiceRole ARN with Condition iam:PassedToService = backup.amazonaws.com; omit this block entirely if AWS Backup is not used.
arn:aws:iam::aws:policy/ReadOnlyAccessIAM policySufficient for audit-only runs that report on bucket, volume, file system, database and table configuration without changing anything.
โณ Preferred starting point: attach this alone first, run the inventory step, and add write actions only for the specific resources the task must change.
Step by step
- 1
Install and verify AWS CLI v2
Install from the official installer, then confirm the major version: 'aws --version' must print aws-cli/2.x. Version 1 does not support --checksum-algorithm on s3 sync or --object-attributes on get-object-attributes.
Open in the console / docs โ - 2
Configure credentials (prefer IAM Identity Center over static keys)
Run 'aws configure sso --profile storage-admin', follow the browser login, then 'aws sso login --profile storage-admin'. For static keys instead: 'aws configure --profile storage-admin' and paste AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY.
Open in the console / docs โ - 3
Pin the region and the expected account id
Run 'aws configure set region us-east-2 --profile storage-admin', then export the safety guard: 'export AWS_PROFILE=storage-admin', 'export AWS_REGION=us-east-2', 'export AWS_EXPECTED_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)'.
Open in the console / docs โ - 4
Create the least-privilege IAM policy
In the IAM console choose Policies > Create policy > JSON, paste one statement per service block from the Required access table, and scope each Resource to the specific bucket, volume, file system, DB and table ARNs. Attach the policy to the role or user this skill runs as. Start with ReadOnlyAccess only and add write actions per task.
Open in the console / docs โ - 5
Grant KMS key access for encrypted resources
For every customer-managed key protecting an in-scope bucket, volume, snapshot or database, edit the key policy (KMS console > Customer managed keys > <key> > Key policy) to allow the skill principal kms:Decrypt, kms:GenerateDataKey and kms:CreateGrant. Cross-account and cross-region snapshot copies fail without this.
Open in the console / docs โ - 6
Install boto3 for the scripted paths
Run 'python -m pip install --upgrade "boto3>=1.34"' and confirm with 'python -c "import boto3;print(boto3.__version__)"'. Install jq as well for the reference bash script.
Open in the console / docs โ - 7
Rehearse permissions before the first real change
Run 'aws iam simulate-principal-policy --policy-source-arn $(aws sts get-caller-identity --query Arn --output text) --action-names s3:PutBucketVersioning ec2:CreateSnapshot rds:CreateDBSnapshot dynamodb:UpdateContinuousBackups --query "EvaluationResults[].[EvalActionName,EvalDecision]" --output table' and confirm every row reads allowed.
Open in the console / docs โ - 8
Confirm dry-run behaviour works
Run 'aws ec2 create-snapshot --volume-id vol-0123456789abcdef0 --dry-run'. The expected, correct outcome is the error 'An error occurred (DryRunOperation) ... Request would have succeeded'. Seeing UnauthorizedOperation means the policy is incomplete.
Open in the console / docs โ
Check it worked
aws sts get-caller-identity --output json && aws configure get region && aws s3api list-buckets --query 'length(Buckets)' --output text
If you hit an error
An error occurred (AccessDenied) when calling the PutBucketLifecycleConfiguration operation: Access DeniedCause: The attached policy lacks s3:PutLifecycleConfiguration for that bucket ARN, or an SCP / bucket policy explicitly denies it.
Fix: Add s3:PutLifecycleConfiguration on arn:aws:s3:::<bucket>, then confirm with 'aws iam simulate-principal-policy --policy-source-arn <caller-arn> --action-names s3:PutLifecycleConfiguration --resource-arns arn:aws:s3:::<bucket>'. Check for an organization SCP if the simulation says allowed but the call still fails.
An error occurred (UnauthorizedOperation) when calling the CreateSnapshot operation: You are not authorized to perform this operation.Cause: Missing ec2:CreateSnapshot, or a Condition (tag, region) on the policy that the request does not satisfy.
Fix: Grant ec2:CreateSnapshot and ec2:CreateTags. Re-run with --dry-run; a successful rehearsal returns DryRunOperation instead. Decode the encoded message with 'aws sts decode-authorization-message --encoded-message <value>'.
An error occurred (DryRunOperation) when calling the CreateSnapshot operation: Request would have succeeded, but DryRun flag is set.Cause: Not a failure. This is the expected success signal from an EC2 dry run.
Fix: Treat it as authorization proof, report it, and re-run without --dry-run only after explicit user confirmation.
An error occurred (ExpiredToken) when calling the ListBuckets operation: The provided token has expired.Cause: IAM Identity Center session or assume-role session credentials aged out (default 1 hour for AssumeRole, 8 hours for SSO).
Fix: Run 'aws sso login --profile storage-admin', or re-issue 'aws sts assume-role' and refresh AWS_SESSION_TOKEN. Long jobs should refresh credentials rather than start a copy that will die halfway.
An error occurred (InvalidClientTokenId) when calling the GetCallerIdentity operation: The security token included in the request is invalid.Cause: Deleted or rotated access key, a typo in AWS_ACCESS_KEY_ID, or ASIA temporary keys supplied without AWS_SESSION_TOKEN.
Fix: Re-check the key in IAM > Users > Security credentials. If the key starts with ASIA you must also export AWS_SESSION_TOKEN.
An error occurred (KMSKeyNotAccessibleFault) when calling the CopyDBSnapshot operationCause: The destination KMS key policy does not trust the calling principal, or a cross-region copy referenced a key ARN from the source region.
Fix: Use a key that exists in the destination region and add kms:Decrypt, kms:GenerateDataKey and kms:CreateGrant for the caller in that key's key policy. Same fix applies to ec2 copy-snapshot.
An error occurred (DBSnapshotAlreadyExists) when calling the CreateDBSnapshot operationCause: The snapshot identifier is already used in this account and region; identifiers must be unique and are not reused after deletion within the retention window.
Fix: Append a UTC timestamp: --db-snapshot-identifier "$DB-manual-$(date -u +%Y%m%d%H%M%S)".
An error occurred (InvalidParameterCombination) when calling the RestoreDBInstanceToPointInTime operation: Cannot specify both RestoreTime and UseLatestRestorableTimeCause: Both a fixed restore timestamp and the latest-restorable flag were passed.
Fix: Pass exactly one: --restore-time 2026-07-20T03:15:00Z or --use-latest-restorable-time. Check the valid window first with 'aws rds describe-db-instances --query "DBInstances[].LatestRestorableTime"'.
An error occurred (ContinuousBackupsUnavailableException) when calling the UpdateContinuousBackups operationCause: PITR is still being enabled or disabled on the table; the setting is not instantaneous.
Fix: Poll 'aws dynamodb describe-continuous-backups --table-name <table>' until ContinuousBackupsStatus is ENABLED, then retry.
An error occurred (LimitExceededException) when calling the RestoreTableToPointInTime operationCause: Too many concurrent table control-plane operations (restores, creates, deletes) in the region.
Fix: Serialize restores, wait for TableStatus to reach ACTIVE via 'aws dynamodb wait table-exists --table-name <target>', then start the next one.
An error occurred (VolumeModificationRateExceeded) when calling the ModifyVolume operationCause: An EBS volume was modified again inside the mandatory cooldown after the previous modification.
Fix: Check 'aws ec2 describe-volumes-modifications --volume-ids <vol>' until ModificationState is completed, then wait out the cooldown (up to 6 hours) before the next change.
fatal error: An error occurred (InvalidRequest) when calling the PutObject operation: The bucket does not allow ACLsCause: The destination bucket uses Bucket owner enforced object ownership, but the copy passed --acl.
Fix: Drop the --acl flag from the s3 cp/sync command and control access with a bucket policy instead.
An error occurred (BucketAlreadyOwnedByYou) when calling the CreateBucket operationCause: The bucket already exists in your account; create-bucket is not idempotent outside us-east-1.
Fix: Skip creation and proceed to configuration. Detect first with 'aws s3api head-bucket --bucket <name>'.
An error occurred (SlowDown) / (503 Slow Down) when calling the PutObject operationCause: Request rate exceeded the per-prefix S3 limit during a large sync.
Fix: Lower concurrency with 'aws configure set default.s3.max_concurrent_requests 10', spread keys across prefixes, and rely on the CLI's adaptive retries rather than restarting the whole sync.
Official documentation: https://docs.aws.amazon.com/cli/latest/reference/ โ
API operations this skill uses (20)
Every call the skill makes, linked to its official reference page.
| Operation | Call | Permission | Ref |
|---|---|---|---|
| List all S3 buckets in the account Inventory step: discover in-scope buckets before proposing any configuration change. | CLI aws s3api list-buckets --query 'Buckets[].Name' --output text | s3:ListAllMyBuckets | docs โ |
| Enable S3 bucket versioning Creates a recovery point for object overwrites and deletes before any migration or lifecycle change. | CLI aws s3api put-bucket-versioning --bucket "$B" --versioning-configuration Status=Enabled | s3:PutBucketVersioning | docs โ |
| Set default bucket encryption to SSE-KMS with S3 Bucket Keys Guarantees every new object is encrypted with a customer-managed key, with BucketKeyEnabled to cut KMS request cost. | CLI aws s3api put-bucket-encryption --bucket "$B" --server-side-encryption-configuration file://sse.json | s3:PutEncryptionConfiguration | docs โ |
| Apply an S3 lifecycle configuration Tiers objects to STANDARD_IA / GLACIER_IR / DEEP_ARCHIVE, expires noncurrent versions and aborts stale multipart uploads. | CLI aws s3api put-bucket-lifecycle-configuration --bucket "$B" --lifecycle-configuration file://lifecycle.json | s3:PutLifecycleConfiguration | docs โ |
| Enforce S3 Block Public Access Baseline hardening applied before any data is written to a destination bucket. | CLI aws s3api put-public-access-block --bucket "$B" --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true | s3:PutBucketPublicAccessBlock | docs โ |
| Read object size and SHA-256 checksum without downloading Integrity verification after a migration; ETag is unreliable for multipart objects, ChecksumSHA256 is not. | CLI aws s3api get-object-attributes --bucket "$B" --key "$K" --object-attributes ETag Checksum ObjectSize | s3:GetObjectAttributes | docs โ |
| Dry-run a bucket-to-bucket sync Shows exactly which objects would be copied, and how many, before any write occurs. | CLI aws s3 sync s3://$SRC s3://$DST --dryrun | s3:ListBucket | docs โ |
| Copy objects with SHA-256 checksums Performs the migration while recording a verifiable additional checksum on every destination object. | CLI aws s3 sync s3://$SRC s3://$DST --checksum-algorithm SHA256 --only-show-errors | s3:GetObject and s3:PutObject | docs โ |
| Create an EBS snapshot (authorization rehearsal first) Creates the recovery point required before any volume modification; --dry-run proves authorization without side effects. | CLI aws ec2 create-snapshot --volume-id "$VOL" --description "pre-change" --tag-specifications 'ResourceType=snapshot,Tags=[{Key=Purpose,Value=pre-change}]' --dry-run | ec2:CreateSnapshot | docs โ |
| Copy an EBS snapshot cross-region and re-encrypt Places a restorable copy in the DR region; required before a cross-region create-volume. | CLI aws ec2 copy-snapshot --source-region us-east-1 --source-snapshot-id "$SNAP" --destination-region us-west-2 --encrypted --kms-key-id "$KMS" --description "dr copy" | ec2:CopySnapshot | docs โ |
| Restore an EBS volume from a snapshot Recreates a volume from a recovery point into a chosen AZ, type and performance profile. | CLI aws ec2 create-volume --snapshot-id "$SNAP" --availability-zone us-east-2a --volume-type gp3 --iops 3000 --throughput 250 --encrypted --kms-key-id "$KMS" | ec2:CreateVolume | docs โ |
| Resize or convert an EBS volume to gp3 Grows capacity or migrates gp2 to gp3 online; must be followed by an OS-level filesystem resize. | CLI aws ec2 modify-volume --volume-id "$VOL" --size 500 --volume-type gp3 --iops 3000 --throughput 250 | ec2:ModifyVolume | docs โ |
| Set EFS lifecycle management policies Moves cold files to Infrequent Access and Archive tiers; the call replaces the entire policy list. | CLI aws efs put-lifecycle-configuration --file-system-id "$FS" --lifecycle-policies '[{"TransitionToIA":"AFTER_30_DAYS"},{"TransitionToArchive":"AFTER_90_DAYS"},{"TransitionToPrimaryStorageClass":"AFTER_1_ACCESS"}]' | elasticfilesystem:PutLifecycleConfiguration | docs โ |
| Enable automatic EFS backups Turns on the AWS Backup default plan for the file system so a recovery point exists before any data change. | CLI aws efs put-backup-policy --file-system-id "$FS" --backup-policy Status=ENABLED | elasticfilesystem:PutBackupPolicy | docs โ |
| Create a manual RDS snapshot Takes a retained recovery point before schema changes, engine upgrades or data migration. | CLI aws rds create-db-snapshot --db-instance-identifier "$DB" --db-snapshot-identifier "$DB-manual-$(date -u +%Y%m%d%H%M%S)" | rds:CreateDBSnapshot | docs โ |
| Restore an RDS instance to a point in time Recovers data to a moment before an incident into a NEW identifier, leaving the source untouched. | CLI aws rds restore-db-instance-to-point-in-time --source-db-instance-identifier "$DB" --target-db-instance-identifier "$DB-restore" --restore-time 2026-07-20T03:15:00Z --no-publicly-accessible --db-subnet-group-name "$SUBNET_GROUP" | rds:RestoreDBInstanceToPointInTime | docs โ |
| Change RDS automated backup retention Sets the PITR window; moving from 0 to a positive value enables backups and briefly restarts single-AZ instances. | CLI aws rds modify-db-instance --db-instance-identifier "$DB" --backup-retention-period 14 --apply-immediately | rds:ModifyDBInstance | docs โ |
| Enable DynamoDB point-in-time recovery Provides a 35-day continuous recovery window; required before any bulk write or migration against the table. | CLI aws dynamodb update-continuous-backups --table-name "$TABLE" --point-in-time-recovery-specification PointInTimeRecoveryEnabled=true | dynamodb:UpdateContinuousBackups | docs โ |
| Restore a DynamoDB table to a point in time Recovers table data into a new table; auto scaling, TTL, tags and streams must be re-applied afterwards. | CLI aws dynamodb restore-table-to-point-in-time --source-table-name "$TABLE" --target-table-name "$TABLE-restore" --use-latest-restorable-time | dynamodb:RestoreTableToPointInTime | docs โ |
| Export a DynamoDB table to S3 for verification or analytics Produces a consistent offline copy for migration verification without consuming table read capacity. | CLI aws dynamodb export-table-to-point-in-time --table-arn "$TABLE_ARN" --s3-bucket "$B" --s3-prefix exports/ --export-format DYNAMODB_JSON | dynamodb:ExportTableToPointInTime | docs โ |