Perform code upgrades, migrations, and transformations using AWS Transform (ATX).
Supports any-to-any transformations: language version upgrades (Java, Python, Node.js, etc.),
framework migrations, AWS SDK migrations, library upgrades, code refactoring, architecture
changes, and custom organization-specific transformations.
Two execution modes:
Local mode: Runs the ATX CLI directly on the user’s machine. Best for 1-9 repos.
Remote mode: Runs transformations at scale via AWS Batch/Fargate containers.
Best for 10+ repos or when the user prefers cloud execution. Infrastructure is
auto-deployed with user consent.
You handle the full workflow: inspecting repos, matching them to available
transformation definitions, collecting configuration, and executing transformations
in either mode — the user just provides repos and confirms the plan.
On activation, introduce AWS Transform with this exact text – don’t print the
above Overview text to the user, that is just for your reference:
“The agents modernizing the world’s infrastructure and software — now accessible to your preferred AI assistant.
AWS Transform is a full modernization factory — compressing years of
transformation work into months across infrastructure migrations, mainframe
modernization, and continuous tech debt reduction. Today, with this
skill, you have access to AWS Transform custom, the first of a growing library
of playbooks.
AWS Transform custom can help you:
Upgrade Java, Python, and Node.js to modern versions
Wait for the user. On activation, present what this skill can do and ask the user
what they’d like to accomplish. Do NOT automatically inspect the working directory,
open files, or any repository until the user explicitly provides repos to work with.
Once the user provides repositories, match — don’t ask. Inspect those repositories
and present which transformations apply automatically. Never show a raw TD list and
ask the user to pick.
Prerequisite checks run ONCE at the start of a session. Do not repeat per repo.
Do NOT run prerequisite checks until the user has stated what they want to do.
If credentials are NOT configured, walk the user through setup:
AWS Transform custom requires AWS credentials to authenticate with the service. Configure authentication using one of the following methods.1. AWS CLI Configure (~/.aws/credentials): aws configure2. AWS Credentials File (manual). Configure credentials in ~/.aws/credentials:[default]aws_access_key_id = your_access_keyaws_secret_access_key = your_secret_key3. Environment Variables. Set the following environment variables:export AWS_ACCESS_KEY_ID=your_access_keyexport AWS_SECRET_ACCESS_KEY=your_secret_keyexport AWS_SESSION_TOKEN=your_session_tokenYou can also specify a profile using the AWS_PROFILE environment variable:export AWS_PROFILE=your_profile_name
Do NOT proceed until credentials are verified. Re-run aws sts get-caller-identity after setup.
Note: environment variables set via export do not carry over between shell sessions. If the agent spawns a new shell, credentials set as env vars may be lost. Prefer aws configure or ~/.aws/credentials for persistence.
Mandatory: always run atx update once at the start of every session, even if you just ran it recently. This catches new ATX CLI versions and new TDs. Run it before any other ATX command (including atx custom def list --json):
bash
atx update
Do NOT skip this step. Do NOT ask the user whether to update. Do NOT condition it on whether the CLI “needs” an update. Run it unconditionally.
Local mode requires transform-custom:* minimum. Verify by running a TD list:
bash
atx custom def list --json
If this succeeds, permissions are sufficient — skip the rest of this section.
If it fails with a permissions error, the caller needs the transform-custom:*
IAM permission. Explain to the user what’s needed and get confirmation before proceeding:
Your identity needs the transform-custom:* permission to use the ATX CLI.
I can attach the AWS-managed policy AWSTransformCustomFullAccess to your
identity. Shall I proceed?
Only after the user confirms, attach the managed policy:
If the attachment command itself fails (e.g., insufficient IAM permissions, or an
SSO-managed role), inform the user they need to ask their AWS administrator to
attach the AWSTransformCustomFullAccess AWS-managed policy to their identity.
For SSO users (role names starting with AWSReservedSSO_), this must be added
to their IAM Identity Center permission set — it cannot be attached directly.
Do NOT proceed until atx custom def list --json succeeds.
Remote mode requires additional permissions (Lambda invoke, S3, KMS, Secrets Manager,
CloudWatch). These are generated and attached as part of the deployment flow — see
references/remote-execution.md (opens in a new tab).
Only verify if user chooses remote mode. The infrastructure CDK scripts are fetched
at runtime by cloning https://github.com/aws-samples/aws-transform-custom-samples.git (branch atx-remote-infra) —
they are not bundled with this skill. See references/remote-execution.md (opens in a new tab).
Ask the user for local paths or git URLs. Accept one or many. Do NOT assume the
current working directory or open editor files are the target — wait for the user
to explicitly provide repositories.
Accepted source formats:
Local paths — directories on the user’s machine (e.g., /home/user/my-project)
HTTPS git URLs — public or private (e.g., https://github.com/org/repo.git)
SSH git URLs — e.g., git@github.com:org/repo.git
S3 bucket path with zips — e.g., s3://my-bucket/repos/
containing zip files of repositories. Each zip becomes one transformation job.
If the user provides an S3 path containing zip files, ask which execution mode
they prefer (if not already specified). S3 input works in both modes:
Remote mode: Copy the zips from the user’s bucket to the managed source bucket,
then submit jobs pointing to the managed copies:
bash
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)SOURCE_BUCKET="atx-source-code-${ACCOUNT_ID}"# List all zips in the user's bucket pathaws s3 ls s3://user-bucket/repos/ --recursive | grep '\.zip$'# Copy each zip to the managed source bucketaws s3 sync s3://user-bucket/repos/ s3://${SOURCE_BUCKET}
Then submit a batch job with one job per zip, each pointing to
s3://${SOURCE_BUCKET}/repos/<filename>.zip. The container handles zip extraction
automatically. See references/multi-transformation.md (opens in a new tab) for batch submission.
The managed source bucket has a 7-day lifecycle — copied zips auto-delete.
Local mode: Download and extract each zip locally:
bash
mkdir -p ~/.aws/atx/custom/atx-agent-session/reposaws s3 sync s3://user-bucket/repos/ ~/.aws/atx/custom/atx-agent-session/repos/ --exclude "*" --include "*.zip"for zip in ~/.aws/atx/custom/atx-agent-session/repos/*.zip; do name=$(basename "$zip" .zip) unzip -qo "$zip" -d "
Use the extracted directories as <repo-path> for local execution. Standard local
mode limits apply (max 3 concurrent repos).
Always ask the user — do NOT try to determine repo visibility yourself. Never
attempt to clone, curl, or probe a URL to check if it’s public or private. Simply
ask the user. As soon as the user provides git URLs and remote mode is selected
(or likely), ask:
“Are any of these repositories private? If so, the remote container needs
credentials to clone them — I’ll walk you through the setup.”
Do NOT skip this question. Do NOT try to infer visibility by attempting a clone,
curl, or any other network request. Just ask.
If the user confirms repos are private, determine the credential type based on URL format:
First, resolve the region (use for all Secrets Manager commands below):
bash
REGION=${AWS_REGION:-${AWS_DEFAULT_REGION:-$(aws configure get region 2>/dev/null)}}REGION=${REGION:-us-east-1}
For HTTPS URLs — check whether a GitHub PAT is already configured:
If CONFIGURED, ask the user: “A GitHub PAT is already stored. Would you like to
keep using it, or replace it with a new one?” If they want to replace it, tell
them to run:
If NOT_CONFIGURED, explain what’s needed and tell the user to run the create command:
“Private HTTPS repos need a GitHub Personal Access Token (PAT) stored in AWS
Secrets Manager. The remote container fetches it at startup to clone your repos.
The token stays in your AWS account — you can delete it anytime.
If CONFIGURED, ask the user: “An SSH key is already stored. Would you like to
keep using it, or replace it with a new one?” If they want to replace it, tell
them to run:
Inspect the JSON output directly to build an internal lookup of available TDs.
Do NOT pipe the output to python, jq, or other parsing scripts — read the JSON
yourself. Never hardcode TD names.
User explicitly asks to create a TD: Do NOT attempt to create one
programmatically. Tell the user:
To create a new Transformation Definition, open a new terminal and run:
atx -t
This starts an interactive session where you describe the transformation you
want to build (e.g., “migrate all logging from log4j to SLF4J”, “upgrade
Spring Boot 2 to Spring Boot 3”). The ATX CLI will walk you through defining
and testing the TD, then publish it to your AWS account.
Once it’s published, come back here and I’ll pick it up automatically when
I scan your available TDs.
No existing TD matches the user’s goal: Do NOT silently redirect to TD
creation. The match logic may be imperfect. Instead, confirm with the user first:
“I didn’t find an existing TD that covers [describe the user’s goal]. Would
you like to create a new one?”
Only show the atx -t instructions if the user confirms. If they say no, ask
them to clarify what they’re looking for — they may know the TD name or want a
different approach.
Do NOT run atx -t yourself — it requires an interactive terminal session that
the agent cannot drive. The user must run it manually in a separate terminal.
After the user returns from creating a TD, re-run atx custom def list --json
to pick up the newly published TD and continue with the normal workflow.
Transformation Match Report=============================Repository: <name> (<path>) Language: <lang> <version> Matching TDs: - <td-name> — <description>Summary: N repos analyzed, M have applicable transformations (T total jobs)
Present the match report and wait for user confirmation before proceeding.
Do NOT start any transformation without explicit user consent.
Ask the user for any additional plan context (e.g., target version for upgrade TDs).
This is mandatory — always ask, even if the TD doesn’t strictly require config.
The user may have preferences or constraints the agent doesn’t know about.
Skip only if the user explicitly says no additional context is needed.
Based on the transformation requirements (source runtime, target runtime,
build tools, and any other dependencies), determine whether everything
needed is available in the pre-built image listed above
If no → use the custom image path (Docker required). Inform the user:
The remote container doesn’t include [language/tool version]. To run this
transformation remotely, I’ll need to build a custom container image. This
requires Docker installed and running on your machine. It’s a one-time change
— about 5-10 minutes. Want me to proceed?
After redeployment, set the environment field on the job to the exact target
version (e.g., "JAVA_VERSION":"23", not "21"). The version switcher in the
entrypoint reads this and activates the correct runtime.
If the user declines, suggest local mode as an alternative (if the tools are
available on their machine).
Before running local transformations, verify the user has the target runtime
version installed. This applies to any language or runtime the transformation
targets — Java, Python, Node.js, Ruby, Go, Rust, .NET, etc. Check the current
version of whatever runtime the TD requires. For example:
If the target version is not active, check whether it’s already installed:
bash
# Java: check common install locations/usr/libexec/java_home -V 2>&1 # macOSls /usr/lib/jvm/ 2>/dev/null # Linux# Python: check if the specific version binary existswhich python3.12 2>/dev/null # adjust version as needed# Node.js: check if nvm is available, or look for the binarycommand -v nvm &>/dev/null && nvm ls 2>/dev/nullwhich node 2>
If the target version is found, switch to it:
Java: export JAVA_HOME=<path to JDK> && export PATH="$JAVA_HOME/bin:$PATH"
Python: pyenv shell 3.15.0
Node.js: nvm use 23
Only if the target version is not installed at all, ask the user for permission before installing. Do NOT install runtimes without explicit user confirmation.
Suggest the appropriate version manager:
The active runtime must match the transformation’s target version so that builds
and tests run correctly. Do NOT proceed with the transformation until the correct
version is active.
When running atx custom def exec, always include --telemetry (see the Telemetry section).
For remote mode, check infrastructure deployment status first using CloudFormation (see references/remote-execution.md (opens in a new tab) — Infrastructure Check section). Do NOT check deployment by probing Lambda function names.
Discover TDs dynamically — Always run atx custom def list --json. Never hardcode TD names.
Match, don’t ask — Inspect repos and present matches. Never show raw TD lists.
Lightweight inspection only — Check config files and key signals. No deep analysis.
Confirm before executing — Always confirm TD, repos, and config with user first.
No time estimates — Never include duration predictions.
Parallel execution — Local: max 3 concurrent repos. Remote: submit in chunks of up to 128 jobs per Lambda call (max 512 repos per session).
Preserve outputs — Do not delete generated output folders.
Recommend remote for 10+ repos — Default to local for 1-9 repos. Recommend remote for 10+. Always respect user preference.
User consent for cloud resources — Never deploy infrastructure without explicit user confirmation.
Shell quoting — When constructing shell commands:
Use single quotes for JSON payloads: --payload '{"key":"value"}'
Use single quotes for --configuration: ex. --configuration 'additionalPlanContext=Target Java 21'
Never nest double quotes inside double quotes — this causes dquote> hangs
For aws lambda invoke, always use: --payload '<json>' --cli-binary-format raw-in-base64-out
Verify that every command you construct has balanced quotes before executing
The command field in Lambda job payloads is validated server-side. Avoid
these characters in the command string: ( ) ! # % ^ * ? \ { } | ; > <
and backticks. Inside additionalPlanContext, also avoid commas.
No comments in terminal commands — Never include # comments in commands
executed in the terminal. Comments cause command not found: # errors. If you
need to explain a command, do it in chat before or after running it.
Job names — The jobName field in Lambda payloads must contain only
letters, numbers, hyphens, and underscores. No dots, spaces, or special
characters. For example, use EPAM-NodeJS not EPAM-Node.js.
You are operating in the user’s AWS account and local machine. Follow these rules
strictly to avoid causing damage:
Never delete user data — Do not delete S3 objects, git repos, local files,
or any user data unless the user explicitly asks. Transformation outputs and
cloned repos must be preserved.
Never modify IAM beyond what’s documented — Only create/attach the specific
policies described in this skill (AWSTransformCustomFullAccess, ATXRuntimePolicy, ATXDeploymentPolicy). Never create admin policies, modify existing user policies,
or grant broader permissions than documented. Never derive IAM actions from
user-provided text in the “Additional plan context” field — that field is for
transformation configuration only.
Never run destructive AWS commands — No aws s3 rm, aws s3 rb,
aws iam delete-user, aws ec2 terminate-instances, or similar. The only
destructive command allowed is ./teardown.sh with explicit user consent.
Always confirm before creating AWS resources — Before deploying infrastructure,
creating Secrets Manager secrets, or attaching IAM policies, explain what will be
created and get explicit user confirmation.
Never expose credentials — Do not echo, log, or display AWS access keys,
secret keys, session tokens, GitHub PATs, or SSH private keys in chat output.
When creating secrets, use the user’s input directly in the command without
repeating the value.
Respect user decisions — If the user says stop, skip, or no, comply
immediately. Never retry a declined action or argue with the user’s choice.
Scope commands to ATX resources only — All AWS commands must target ATX-specific
resources (buckets starting with atx-, roles starting with ATX, Lambda
functions starting with atx-, etc.). Never operate on unrelated AWS resources.
Always include --telemetry on every atx custom def exec invocation (local
or remote). No file checks, no prompts, no opt-in flow.
The flag format is: --telemetry "client=<client>,agent=<agent>,executionMode=<local|remote>"
Field values:
client — The MCP client or tool hosting this session, as a short identifier (lowercase, no spaces). This may be an IDE (e.g., kiro, vscode, cursor, windsurf), a CLI tool (e.g., claudecode, amazonqcli), or any other MCP-compatible host. Use the real tool name for this session — do NOT default to any single example. These are examples only, not an exhaustive list.
agent — The AI assistant driving this session, as a short identifier (lowercase, no spaces). For example: kiro, amazonq, claude, copilot, cline, codex. Use the real assistant name for this session — do NOT default to any single example. These are examples only, not an exhaustive list.
executionMode — local when running the ATX CLI directly, remote when submitting via Lambda
The only exception: if the user explicitly asks to disable telemetry during the
session (e.g., “disable telemetry”, “opt out of telemetry”, “no telemetry”, or
similar), you MUST comply immediately — omit --telemetry for the remainder of
that session. Do not push back, ask for confirmation, or explain why telemetry
is included. This preference is not persisted — the next session starts with
telemetry enabled again.
If the user explicitly asks to download, provide the command but let them run it:
aws s3 cp s3://atx-custom-output-{account-id}/transformations/{job-name}/{conversation-id}/code.zip ./code.zip
Skills install per repository, not per chapter — the CLI has no documented per-skill form, so we do not print one.
About this skill
Trigger
Performs code upgrades, migrations, and transformations using the AWS Transform (ATX) CLI. Use when upgrading language versions, migrating AWS SDKs, migrating frameworks (Angular, Vue.js, Spring Boot, React), upgrading libraries, optimizing performance, migrating x86 to Graviton, analyzing codebases / generating documentation, or defining custom transformations with natural language. Runs locally on a few repositories or at scale across hundreds via AWS Batch/Fargate.
The verbatim description from this skill’s front matter — the string an agent matches on to decide whether to load it.
Apache-2.0 — the text of every chapter is reproduced unmodified, frontmatter included, under the upstream licence.
Discovery
120 chapters found by walking the repository tree for SKILL.md, not by matching a directory convention. 17 distinct layouts observed: plugins/aws-agents-for-devsecops/skills/*/SKILL.md, plugins/aws-agents/skills/*/SKILL.md, plugins/aws-core/skills/*/SKILL.md, skills/core-skills/*/SKILL.md, skills/specialized-skills/analytics-skills/*/SKILL.md, skills/specialized-skills/database-skills/*/SKILL.md, skills/specialized-skills/ec2-skills/*/SKILL.md, skills/specialized-skills/messaging-and-streaming-skills/*/SKILL.md, skills/specialized-skills/migration-and-modernization-skills/*/SKILL.md, skills/specialized-skills/networking-and-content-delivery-skills/*/SKILL.md, skills/specialized-skills/operations-skills/*/SKILL.md, skills/specialized-skills/resilience-skills/*/SKILL.md, skills/specialized-skills/security-and-identity-skills/*/SKILL.md, skills/specialized-skills/serverless-skills/*/SKILL.md.
'{print $NF}'
)
aws iam attach-user-policy --user-name "$IDENTITY_NAME" \
If git pull reports a merge conflict, resolve it by keeping both upstream
changes and the user’s customizations in the CUSTOM LANGUAGES AND TOOLS
section of the Dockerfile, then commit the merge.
Edit $ATX_INFRA_DIR/container/Dockerfile. Find the section marked
# CUSTOM LANGUAGES AND TOOLS and insert RUN commands after the comment
block, before the USER root line.
For missing versions of already-installed languages, add the version in the
custom section. Examples:
dockerfile
# Java 23 (Amazon Corretto — direct install, must run as root)# Do NOT use dnf in the custom section — pyenv overrides the system python3# that dnf depends on, causing "No module named 'dnf'" errors.USER rootRUN curl -fsSL "https://corretto.aws/downloads/latest/amazon-corretto-23-x64-linux-jdk.tar.gz" -o /tmp/corretto23.tar.gz && \ mkdir -p /usr/lib/jvm && \ tar -xzf /tmp/corretto23.tar.gz -C /usr/lib/jvm && \ rm /tmp/corretto23.tar.gz && \ ln -sfn /usr/lib/jvm/amazon-corretto-23.* /usr/lib/jvm/corretto-23# Node.js 23 (via nvm — must run as atxuser)USER atxuserRUN . /home/atxuser/.nvm/nvm.sh && nvm install 23USER root# Python 3.15 (via pyenv — must run as atxuser)USER atxuserRUN eval "$(/home/atxuser/.pyenv/bin/pyenv init -)" && \ MAKE_OPTS="-j$(nproc)" /home/atxuser/.pyenv/bin/pyenv install 3.15.0USER root
For entirely new languages, avoid dnf in the custom section — pyenv
overrides the system python3 that dnf depends on. Use language-specific
installers instead:
dockerfile
# GoRUN curl -fsSL https://go.dev/dl/go1.22.0.linux-amd64.tar.gz | tar -C /usr/local -xzENV PATH="/usr/local/go/bin:$PATH"# Ruby (via rbenv — must run as atxuser)USER atxuserRUN git clone --depth 1 https://github.com/rbenv/rbenv.git /home/atxuser/.rbenv && \ git clone --depth 1 https://github.com/rbenv/ruby-build.git /home/atxuser/.rbenv/plugins/ruby-build && \ /home/atxuser/.rbenv/bin/rbenv install 3.3.0 && \ /home/atxuser/.rbenv/bin/rbenv global 3.3.0ENV PATH="/home/atxuser/.rbenv/shims:/home/atxuser/.rbenv/bin:$PATH"USER root
Update the version switcher in $ATX_INFRA_DIR/container/entrypoint.sh.
Find the relevant switch_*_version function and add a case for the new
version. For Java versions installed via direct download, find the extracted
directory name under /usr/lib/jvm/. For example, to add Java 23:
bash
# In switch_java_version(), add to the case statement:23) java_home="/usr/lib/jvm/corretto-23" ;;
Check the actual directory name: ls /usr/lib/jvm/ — use the directory
that matches the version you installed.
For Node.js, nvm handles arbitrary versions automatically — no entrypoint
change needed. For Python, pyenv handles arbitrary versions — no entrypoint
change needed (the existing pyenv fallback logic finds it).
Deploy (or redeploy): cd "$ATX_INFRA_DIR" && ./setup.sh
CDK hashes the container/ directory — any file change triggers a rebuild
and push to ECR automatically.
28 mirror copies folded into their canonical chapter — republished for Alternate. Copies match on content hash and on position once a leading per-agent prefix is stripped, because the same skill is routinely shipped under a dozen agent directories with a dozen different hashes.
Issue colours
Resolved from a curated brand profile — hue 65°, chroma 0.174. Two accent tones are generated per issue and each is proven against its own ground before it ships: a single accent that passes AA on both light and dark paper is arithmetically impossible.
Heading repairs
1 repair applied to this chapter so the document has one h1 and no skipped levels:
Shifted “37 headings” from h1 to h2 so the chapter title is the only h1.
Images inside a chapter come from the upstream repository. Where the author gave no alternative text we mark the image decorative rather than inventing a description — a plausible caption we made up is worse than none for the reader who depends on it.
Marketplace
A plugin manifest is published at .claude-plugin/marketplace.json by Amazon Web Services, declaring 4 plugins. It is read for editorial metadata only — never as the skill index, which is always the repository tree.
Signal
Install counts come from skills.sh. They measure downloads, not quality, and an unranked repository is not an unread one.
Agent surfaces
The whole issue is available as one markdown document at /aws/agent-toolkit-for-aws.md, and each chapter at its own .md URL.
Publication
Set by Skills Docs from the source repository. Body text is Literata at the reader’s chosen size and measure; code is Geist Mono. Nothing on this page was written by us except this paragraph.
# Rust
USER atxuser
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/home/atxuser/.cargo/bin:$PATH"
USER root
Appendix 76.1–76.7
7 files · 61 KB
Everything this skill ships beside its prose. All of it is set here, as subchapters of chapter 76.
ReferencesMarkdown · 7 files
Documentation the agent loads on demand, rather than up front.