AWS Builder Center

What Happens Behind the EKS One-Click Cluster Access Button?

A deep dive into the bash script that powers the new "Connect" experience in the Amazon EKS console — how it generates per-cluster kubeconfigs, exports environment variables, and rewrites your shell prompt. I also verify that copying the script reproduces the same workflow on a local machine with zsh.

In April 2026, Amazon EKS released a feature called One-click cluster access through CloudShell . When you click the "Connect" button on an EKS cluster detail page, AWS CloudShell launches automatically with kubectl already configured and ready to operate your cluster.
No kubectl, no AWS CLI, no kubeconfig setup on your local machine — you can operate your cluster from a browser alone. This is genuinely useful for emergency operations, quick sanity checks, and lightweight verification tasks. I tried it, and it really was "one click."
But the moment I connected, one command flashed across the screen and caught my attention:
1
source kubectl-connect <cluster-name>
That's not the AWS CLI. So where does this command come from? Why is it executed with source? Is it setting environment variables? Does it only work inside CloudShell? One question led to another, so I decided to dig in.

TL;DR

  • kubectl-connect is not part of the AWS CLI. It is a bash script pre-installed in CloudShell (/usr/local/bin/kubectl-connect, version 1.0.0).
  • It is a wrapper that calls aws eks update-kubeconfig to generate a kubeconfig, then exports the KUBECONFIG environment variable and rewrites PS1 (the shell prompt) in one shot.
  • The reason it must be run with source is so the exported environment variables propagate to the calling shell. The script even includes a warning path for when it is executed instead of sourced.
  • The "Connect" button appears to inject the command string source kubectl-connect <cluster-name> into CloudShell. Nothing is registered in CloudShell's .bashrc or /etc/profile.d/.
  • Its only dependencies are bash, the AWS CLI, and kubectl — so if you copy the script, it runs on your local machine too. I verified this on macOS with zsh.

Test Environment

ItemValue
AWS Regionap-northeast-1 (Tokyo)
EKS clusterappsignals-fargate (Fargate-only, built with Terraform)
CloudShell regionap-northeast-1
The EKS cluster was reused from another experiment. The focus of this article is the analysis of the kubectl-connect script inside CloudShell; the cluster configuration itself is not the point.

Feature Overview

The key message from the What's New announcement  is that you no longer need to install or configure kubectl, the AWS CLI, or kubeconfig files locally. This helps anyone who needs to inspect a cluster right now: no dev machine, no time for environment setup.
ItemDetails
AvailabilityApril 2026
How to useEKS Console → cluster detail page → "Connect" button
Endpoint supportBoth public and private API server endpoints
Private endpoint caseCloudShell automatically launches a VPC environment
PricingNo additional charge
RegionsAll regions where EKS is available
The point worth highlighting is that both public and private API server endpoints are supported. In production, security requirements often push teams toward private API server endpoints, which traditionally meant maintaining a bastion EC2 instance or AWS Cloud9 (no longer available to new users). With this feature, CloudShell automatically spins up a VPC environment when the cluster uses a private endpoint — the cost and effort of maintaining a bastion host disappear. That alone is a compelling reason to adopt it.
Open an EKS cluster detail page in the console, and you'll see the "Connect" button in the action bar at the top right. (My console is set to Japanese, so the screenshots below show the localized labels — "Connect" appears as 「接続」.)
EKS-Connection-Button

Trying It Out

Clicking "Connect" launches CloudShell at the bottom of the screen and shows a "Run command" confirmation modal (「コマンドを実行」 in the Japanese console). The command field is pre-populated with:
1
source kubectl-connect appsignals-fargate
CloudShell-Connection
Click "Run," and the command executes in the CloudShell terminal. Once it completes, the prompt changes and kubectl is immediately usable:
1
2
3
[k8s: appsignals-fargate] $ kubectl get nodes
NAME STATUS ROLES AGE VERSION
fargate-ip-10-20-1-xxx.ap-northeast-1.compute.internal Ready <none> 3m v1.35.0-eks-xxx
Connected to the cluster with zero local kubectl configuration. Now let's figure out what kubectl-connect actually is, and why it needs source.

Is kubectl-connect Part of the AWS CLI?

AWS CLI commands start with aws, so kubectl-connect clearly isn't one. Let's identify what it is from inside CloudShell:
1
2
3
4
5
6
7
8
$ which kubectl-connect
/usr/local/bin/kubectl-connect

$ type kubectl-connect
kubectl-connect is /usr/local/bin/kubectl-connect

$ declare -f kubectl-connect
(no output)
CommandWhat it tells us
whichThe actual file path on PATH. Shell functions and aliases are not shown.
typeThe kind of command (alias / function / builtin / file).
declare -fThe function body if it's defined as a shell function. No output means it's not a function.
Combining the three results: it's a real file placed directly in /usr/local/bin/, and it is neither a shell function nor an alias. Let's cat it:
1
2
3
4
5
#!/usr/bin/env bash
# kubectl-connect

VERSION="1.0.0"
...
It's a bash script, version 1.0.0. The script is a collection of function definitions with main "$@" at the end. The AWS CLI invocation is neatly wrapped:
1
2
3
4
5
6
7
8
9
aws_cli() {
if command -v timeout >/dev/null 2>&1; then
AWS_MAX_ATTEMPTS=1 timeout "${AWS_CLI_TIMEOUT}s" aws --no-cli-pager "$@"
else
AWS_MAX_ATTEMPTS=1 aws --no-cli-pager \
--cli-connect-timeout "$AWS_CLI_TIMEOUT" \
--cli-read-timeout "$AWS_CLI_TIMEOUT" "$@"
fi
}
So kubectl-connect is a wrapper script around the AWS CLI. With a default timeout of 10 seconds and AWS_MAX_ATTEMPTS=1 (no retries), the design ensures you never wait long after clicking the button.
You can inspect the full script yourself by running cat /usr/local/bin/kubectl-connect in CloudShell.

The Four Things the Script Does

Reading through the main logic, the script performs four operations in one pass.

1. Create a separate kubeconfig per cluster

1
2
3
4
local KUBECONFIG_PATH="$HOME/.kube/config-$CLUSTER_NAME"
...
EKS_ARGS=(eks update-kubeconfig --name "$CLUSTER_NAME" --kubeconfig ~/.kube/config-"$CLUSTER_NAME" --alias "$ALIAS")
aws_cli "${EKS_ARGS[@]}"
Normally, aws eks update-kubeconfig appends to a single ~/.kube/config file. kubectl-connect instead saves one file per cluster: ~/.kube/config-<cluster-name>.
Pros:
  • Each cluster's kubeconfig is independent, so deleting or replacing configuration is a per-file operation.
  • If you want different clusters open in different terminal tabs, you just point KUBECONFIG at a different file per tab, with no context switching required.
  • Refreshing one cluster's credentials never touches another cluster's config.
  • Troubleshooting "which cluster's config broke" becomes trivial.
Cons:
  • To list all clusters with kubectl config get-contexts, you need to join files with colons: KUBECONFIG=$HOME/.kube/config-A:$HOME/.kube/config-B.
  • Ecosystem tools that assume ~/.kube/config (ArgoCD CLI, Helm, Lens, etc.) need an explicit KUBECONFIG.
  • More clusters means more files.
There are trade-offs, but for developers and SREs who move between multiple clusters, the benefits outweigh the costs. In an ephemeral environment like CloudShell, the "one cluster per tab" workflow is especially effective.

2. Export the KUBECONFIG environment variable

1
2
export KUBECONFIG="$KUBECONFIG_PATH"
echo "Environment configured. KUBECONFIG is set to: $KUBECONFIG_PATH"
If you execute a script normally, exported variables die in a subshell and never reach the calling shell. This is why source is required. The script even ships with a friendly warning for this case:
1
2
3
4
5
if ! _is_sourced; then
_kc_log "⚠️ Script was executed, not sourced. KUBECONFIG and PS1 changes will not persist."
_kc_log "To activate in your current shell, run:"
_kc_log " source $CMD_NAME $CLUSTER_NAME"
fi
The sourced-or-executed detection compares BASH_SOURCE[0] against $0:
1
2
3
_is_sourced() {
[[ "${BASH_SOURCE[0]}" != "${0}" ]]
}

3. Rewrite the prompt (PS1) to show the current cluster

1
2
3
local clean_ps1="${PS1:-\$ }"
clean_ps1="${clean_ps1#\[k8s: *\] }"
export PS1="[k8s: $ALIAS] $clean_ps1"
PS1 is the Unix shell variable that defines the primary prompt — the string you see before $ or #. kubectl-connect rewrites it to prepend [k8s: <cluster-name>] after connecting. When you're hopping between clusters, this gives you a constant visual reminder of which cluster you're about to operate on.
The line clean_ps1="${clean_ps1#\[k8s: *\] }" strips any previously set [k8s: ...] prefix (shortest match) before adding the new one — a thoughtful touch that prevents prefixes from stacking up like [k8s: A][k8s: B] $ as you switch clusters.

4. Test connectivity, and roll back KUBECONFIG on failure

1
2
3
4
5
6
7
8
9
10
11
12
13
local OLD_KUBECONFIG="${KUBECONFIG:-}"
export KUBECONFIG="$KUBECONFIG_PATH"

if ! cluster_output=$(kubectl cluster-info --request-timeout=10s 2>&1); then
_kc_error "⚠️ Configuration complete, but unable to connect to cluster $CLUSTER_NAME"
...
if [ -n "$OLD_KUBECONFIG" ]; then
export KUBECONFIG="$OLD_KUBECONFIG"
else
unset KUBECONFIG
fi
return $E_CLUSTER
fi
If connectivity can't be verified, the script restores the previous KUBECONFIG value and fails safe. You are never left in a state where "the kubeconfig was updated but the cluster is actually unreachable."

Does It Work Outside CloudShell?

No CloudShell-specific dependencies

The script depends only on bash, the AWS CLI, kubectl, and standard utilities like mkdir, sed, shopt, and timeout. There is no CloudShell-specific credential mechanism or VPC auto-launch logic in the script body. In principle, any environment where the AWS CLI works should be able to run a copied version of the script.

Nothing hidden in CloudShell's startup configuration

I also checked where kubectl-connect gets loaded from:
1
2
3
4
5
$ grep -i kube ~/.bashrc
(no output)

$
ls /etc/profile.d/ | grep -i kube
(no output)
Nothing kube-related is registered in ~/.bashrc or /etc/profile.d/, and nothing is auto-sourced at CloudShell startup. The script simply sits on PATH.

What the "Connect" button really is (my hypothesis)

Putting it all together, the console's "Connect" button appears to do just one thing: inject the command string source kubectl-connect <cluster-name> into CloudShell's input and run it. This is consistent with the fact that launching CloudShell independently and typing the same command by hand produces the identical result. There doesn't seem to be any special API working behind the scenes — the button is command-input automation, nothing more.

What Changes Before and After

Here is the before/after diff of running source kubectl-connect <cluster-name>:
AspectBeforeAfter
KUBECONFIGempty/home/cloudshell-user/.kube/config-appsignals-fargate
~/.kube/emptyconfig-appsignals-fargate (cluster-specific file) created
PS1\W $[k8s: appsignals-fargate] \W $
kubectlunusable, no context configuredcluster operations immediately available
The execution output shows everything — kubeconfig generation, KUBECONFIG export, connectivity test, quick tips, and the PS1 rewrite — completing in a single command:
1
2
3
4
5
6
7
8
9
10
11
12
13
~ $ source kubectl-connect appsignals-fargate
Configuring kubectl for EKS cluster: appsignals-fargate
Added new context appsignals-fargate to /home/cloudshell-user/.kube/config-appsignals-fargate
Successfully configured kubectl for cluster: appsignals-fargate
Environment configured. KUBECONFIG is set to: /home/cloudshell-user/.kube/config-appsignals-fargate
✅ Successfully connected to cluster appsignals-fargate

🎯 Quick Tips:
• Switch clusters: source kubectl-connect OTHER-CLUSTER
• List clusters that are currently configured: kubectl-connect list
• Get help: kubectl-connect --help

[k8s: appsignals-fargate] ~ $
source-kubectl-connect

Inside the generated kubeconfig

The generated config-appsignals-fargate is a typical EKS kubeconfig. The notable part is the users: section, which uses the exec: plugin format:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
users:
- name: arn:aws:eks:ap-northeast-1:<AWS_ACCOUNT_ID>:cluster/appsignals-fargate
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- --region
- ap-northeast-1
- eks
- get-token
- --cluster-name
- appsignals-fargate
- --output
- json
command: aws
Every time kubectl accesses the cluster, it runs aws eks get-token locally and authenticates with the resulting token. This is exactly the standard format that aws eks update-kubeconfig produces; kubectl-connect does not rewrite it into anything proprietary.

list and --help

The script also ships a list subcommand that shows configured clusters and which one is currently active:
1
2
3
4
5
6
[k8s: appsignals-fargate] ~ $ kubectl-connect list
Configured clusters:
• appsignals-fargate [CURRENT]

To switch to a cluster, run:
source kubectl-connect CLUSTER_NAME
According to --help, options include --region, --role-arn, --alias, --debug, and --version. You can assume a different IAM role with --role-arn or rename the kubeconfig context with --alias, so multi-account and multi-team setups are clearly anticipated.

Running It on macOS with zsh

To test the hypothesis that this works outside CloudShell, I ran the script on macOS, keeping zsh (the default shell) as-is.
Steps:
  1. Save the output of cat /usr/local/bin/kubectl-connect from CloudShell to a local path (e.g., scripts/kubectl-connect).
  2. chmod +x scripts/kubectl-connect
  3. Have the AWS CLI and kubectl installed, and authenticate with aws sso login --profile <profile>.
  4. Run it with source, still in zsh.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
% export AWS_PROFILE=development
% source ./scripts/kubectl-connect appsignals-fargate
Configuring kubectl for EKS cluster: appsignals-fargate
Added new context appsignals-fargate to /Users/xxxxx/.kube/config-appsignals-fargate
Successfully configured kubectl for cluster: appsignals-fargate
Environment configured. KUBECONFIG is set to: /Users/xxxxx/.kube/config-appsignals-fargate
✅ Successfully connected to cluster appsignals-fargate

get_configured_clusters:5: command not found: shopt
get_configured_clusters:6: command not found: shopt
🎯 Quick Tips:
• Switch clusters: source kubectl-connect OTHER-CLUSTER
• List clusters that are currently configured: kubectl-connect list
• Get help: kubectl-connect --help

[k8s: appsignals-fargate] %
Two command not found: shopt warnings appeared, but they are safe to ignore, for three reasons:
  • As the comment at the top of the script states — Strict mode (set -euo pipefail) not used to prevent shell exit when sourced — the script intentionally avoids strict mode, so a missing command doesn't halt execution.
  • The warnings come from get_configured_clusters, a helper function used only to decide whether to show the quick tips. The essential work (kubeconfig generation and the KUBECONFIG export) has already finished.
  • The glob expansion inside that function (configs=(~/.kube/config-*)) matches correctly anyway, because the kubeconfig was created just before, so the missing shopt -s nullglob doesn't change the outcome.
In practice, everything important completed: kubeconfig generation, KUBECONFIG export, the connectivity test, and the PS1 rewrite. The [k8s: appsignals-fargate] prefix was applied while preserving my existing zsh prompt, color codes (%F{cyan}, etc.) and all:
1
2
3
4
5
6
7
8
[k8s: appsignals-fargate] % echo "PS1=$PS1"
PS1=[k8s: appsignals-fargate] %F{cyan}%~%f%F{yellow}${vcs_info_msg_0_}%f %#

[k8s: appsignals-fargate] % kubectl get nodes
NAME STATUS ROLES AGE VERSION
fargate-ip-10-20-15-98.ap-northeast-1.compute.internal Ready <none> 38m v1.35.3-eks-d6694b8
fargate-ip-10-20-20-63.ap-northeast-1.compute.internal Ready <none> 38m v1.35.3-eks-d6694b8
fargate-ip-10-20-29-195.ap-northeast-1.compute.internal Ready <none> 37m v1.35.3-eks-d6694b8
One more detail: while ~/.kube/config-appsignals-fargate was newly created, my pre-existing ~/.kube/config was left completely untouched. Thanks to the per-cluster file separation, you can add new clusters without any risk of breaking configurations already in use.

Why does list run without warnings?

With the script on PATH, kubectl-connect list produced no shopt warnings. The reason is the execution mode:
How it runsWhich shell executes itshopt behavior
source ./kubectl-connect ... (from zsh)The calling zsh itselfshopt doesn't exist in zsh → warning
kubectl-connect list (via PATH)A new bash process, per the #!/usr/bin/env bash shebangbash, so it works fine
The connect flow must be sourced so environment variables reach the calling shell, which means zsh evaluates it when you call it from zsh. Subcommands like list, --help, and --version don't need that, so running them as normal commands executes them in bash, warning-free. In day-to-day use, the warnings only ever appear on the initial connect, which is easy to live with.
For reference, shopt is a bash builtin; the zsh equivalent is setopt. Fully native zsh support would require swapping in setopt null_glob, but the script is perfectly usable as-is.

Conclusion

Behind the "Connect" button, a bash script runs four steps: generate a kubeconfig, export an environment variable, rewrite the prompt, and verify connectivity. The button itself is a thin mechanism that merely feeds this script into CloudShell.
Once you can read the implementation, bringing it to your local environment becomes an obvious next step. The pattern of per-cluster kubeconfig files (~/.kube/config-<name>) plus a cluster-name prompt prefix translates directly to everyday multi-cluster operations. What looked like a "CloudShell-only feature" turns out to be an experience you can reproduce in your own terminal just by copying one script — that was the real takeaway from this investigation.

References

Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article