Skip to content

Instantly share code, notes, and snippets.

@ipmb
Last active March 21, 2025 17:20
Show Gist options
  • Select an option

  • Save ipmb/e8dc255da2e5cae8438dc2f7d100c8b5 to your computer and use it in GitHub Desktop.

Select an option

Save ipmb/e8dc255da2e5cae8438dc2f7d100c8b5 to your computer and use it in GitHub Desktop.

Revisions

  1. ipmb revised this gist Feb 10, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ecs-connect.py
    Original file line number Diff line number Diff line change
    @@ -94,7 +94,7 @@ def main():
    clusters = get_clusters(ecs)

    if not clusters:
    rprint("[red]No ECS clusters found in region {region}[/red]")
    rprint(f"[red]No ECS clusters found in region {region}[/red]")
    sys.exit(1)

    questions = [
  2. ipmb revised this gist Feb 10, 2025. 1 changed file with 0 additions and 0 deletions.
    Empty file modified ecs-connect.py
    100644 → 100755
    Empty file.
  3. ipmb revised this gist Feb 10, 2025. 1 changed file with 38 additions and 2 deletions.
    40 changes: 38 additions & 2 deletions ecs-connect.py
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,6 @@
    # ]
    # ///


    import os
    import sys

    @@ -39,14 +38,45 @@ def get_tasks(ecs, cluster):
    for task in task_details["tasks"]:
    task_id = task["taskArn"].split("/")[-1]
    task_definition = task["taskDefinitionArn"].split("/")[-1]
    containers = task["containers"]
    running_containers = [
    c for c in containers if c["lastStatus"] == "RUNNING"
    ]

    tasks[task_id] = {
    "label": f"{task_definition} ({task_id})",
    "arn": task["taskArn"],
    "containers": containers,
    "running_containers": running_containers,
    }

    return tasks


    def select_container(containers):
    if not containers:
    rprint("[red]No running containers found in the task[/red]")
    sys.exit(1)

    if len(containers) == 1:
    return containers[0]

    container_choices = [(c["name"], c) for c in containers]
    container_question = [
    inquirer.List(
    "container",
    message="Select container to connect to",
    choices=container_choices,
    )
    ]

    container_answer = inquirer.prompt(container_question)
    if not container_answer:
    sys.exit(1)

    return container_answer["container"]


    def main():
    console = Console()

    @@ -97,8 +127,12 @@ def main():

    task = tasks[task_answer["task"]]

    # Handle container selection
    selected_container = select_container(task["running_containers"])
    container_name = selected_container["name"]

    rprint(
    f"[green]Connecting to task {task["label"]} in cluster {cluster}...[/green]"
    f"[green]Connecting to container {container_name} in task {task['label']} in cluster {cluster}...[/green]"
    )

    os.execvp(
    @@ -111,6 +145,8 @@ def main():
    cluster,
    "--task",
    task["arn"],
    "--container",
    container_name,
    "--command",
    "/bin/bash",
    "--interactive",
  4. ipmb created this gist Feb 6, 2025.
    27 changes: 27 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    # ecs-connect.py

    Get shell access to ECS tasks using execute-command.

    ## Local Prerequisites

    1. `aws` CLI
    2. `uv`
    3. [Install the Session Manager Plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html)
    4. AWS Credentials

    ## ECS Prerequisites

    1. ECS Task Role has [the necessary permissions](https://iam-policy-library.apppack.io/ecs/#ecs-exec)
    2. ECS Task started with [`--enable-execute-command`](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-exec.html#ecs-exec-enabling-and-using#ecs-exec-enabling)

    ## Installation

    `chmod +x ecs-connect.py`

    ## Usage

    ```
    ./ecs-connect.py
    ```

    It will prompt you to choose a cluster and task to connect to.
    126 changes: 126 additions & 0 deletions ecs-connect.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,126 @@
    #!/usr/bin/env -S uv run --script

    # /// script
    # requires-python = ">=3.12"
    # dependencies = [
    # "boto3",
    # "rich",
    # "inquirer",
    # ]
    # ///


    import os
    import sys

    import boto3
    import inquirer
    from rich import print as rprint
    from rich.console import Console


    def get_clusters(ecs):
    clusters = []
    paginator = ecs.get_paginator("list_clusters")

    for page in paginator.paginate():
    clusters.extend([arn.split("/")[-1] for arn in page["clusterArns"]])

    return clusters


    def get_tasks(ecs, cluster):
    tasks = {}
    paginator = ecs.get_paginator("list_tasks")

    for page in paginator.paginate(cluster=cluster):
    if page["taskArns"]:
    task_details = ecs.describe_tasks(cluster=cluster, tasks=page["taskArns"])
    for task in task_details["tasks"]:
    task_id = task["taskArn"].split("/")[-1]
    task_definition = task["taskDefinitionArn"].split("/")[-1]
    tasks[task_id] = {
    "label": f"{task_definition} ({task_id})",
    "arn": task["taskArn"],
    }

    return tasks


    def main():
    console = Console()

    try:
    ecs = boto3.client("ecs")
    session = boto3.Session()
    region = session.region_name

    if not region:
    rprint(
    "[red]No AWS region configured. Please set AWS_REGION or configure your AWS CLI.[/red]"
    )
    sys.exit(1)

    clusters = get_clusters(ecs)

    if not clusters:
    rprint("[red]No ECS clusters found in region {region}[/red]")
    sys.exit(1)

    questions = [
    inquirer.List("cluster", message="Select ECS cluster", choices=clusters),
    ]

    answers = inquirer.prompt(questions)
    if not answers:
    sys.exit(1)

    cluster = answers["cluster"]
    tasks = get_tasks(ecs, cluster)

    if not tasks:
    rprint(f"[red]No tasks found in cluster {cluster}[/red]")
    sys.exit(1)

    task_choices = [(v["label"], k) for k, v in tasks.items()]
    task_question = [
    inquirer.List(
    "task",
    message="Select task to connect to",
    choices=task_choices,
    )
    ]

    task_answer = inquirer.prompt(task_question)
    if not task_answer:
    sys.exit(1)

    task = tasks[task_answer["task"]]

    rprint(
    f"[green]Connecting to task {task["label"]} in cluster {cluster}...[/green]"
    )

    os.execvp(
    "aws",
    [
    "aws",
    "ecs",
    "execute-command",
    "--cluster",
    cluster,
    "--task",
    task["arn"],
    "--command",
    "/bin/bash",
    "--interactive",
    ],
    )

    except Exception as e:
    rprint(f"[red]Error: {str(e)}[/red]")
    sys.exit(1)


    if __name__ == "__main__":
    main()