Created
January 30, 2025 10:04
-
-
Save leiserfg/23da9cbbfc7d9bcf37c2a2fe94e5b5fb to your computer and use it in GitHub Desktop.
Extract secrets from containers in ecs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env -S uv -q run --script | |
# /// script | |
# dependencies = [ | |
# "boto3", | |
# "pzp", | |
# ] | |
# /// | |
import boto3 | |
from pzp import pzp | |
from sys import stderr | |
ecs_client = boto3.client("ecs") | |
ssm_client = boto3.client("ssm") | |
secrets_client = boto3.client("secretsmanager") | |
def get_resolved_secret(secret_arn): | |
"""Retrieve the secret value from either AWS Secrets Manager or SSM Parameter Store.""" | |
try: | |
if secret_arn.startswith("arn:aws:secretsmanager:"): | |
# Secret is from AWS Secrets Manager | |
response = secrets_client.get_secret_value(SecretId=secret_arn) | |
return response["SecretString"] | |
elif secret_arn.startswith("arn:aws:ssm:"): | |
# Secret is from SSM Parameter Store | |
parameter_name = ( | |
"/" + secret_arn.split(":parameter/")[-1] | |
) # Extract parameter name | |
response = ssm_client.get_parameter( | |
Name=parameter_name, WithDecryption=True | |
) | |
return response["Parameter"]["Value"] | |
else: | |
raise ValueError(f"Unsupported secret source for ARN: {secret_arn}") | |
except Exception as e: | |
return f"Error retrieving secret: {e}" | |
def print_container_details(container): | |
if "environment" in container: | |
for env_var in container["environment"]: | |
print(f"{env_var['name']}={env_var['value']}") | |
# Print resolved secrets | |
if "secrets" in container: | |
for secret in container["secrets"]: | |
secret_name = secret["name"] | |
secret_arn = secret["valueFrom"] | |
secret_value = get_resolved_secret(secret_arn) | |
print(f"{secret_name}={secret_value}") | |
def select_task_definition(): | |
# List all task definitions, sorted by descending order (newest first) | |
paginator = ecs_client.get_paginator("list_task_definitions") | |
task_definitions = [] | |
for page in paginator.paginate(sort="DESC"): | |
task_definitions.extend(page["taskDefinitionArns"]) | |
# Filter to keep only the latest version of each task definition family | |
unique_families = set() | |
latest_task_defs = [] | |
for task_def in task_definitions: | |
family = task_def.split("/")[-1].split(":")[0] # Extract family name | |
if family not in unique_families: | |
unique_families.add(family) | |
latest_task_defs.append(task_def) | |
selected_task_def = pzp(candidates=latest_task_defs, lazy=True) | |
if not selected_task_def: | |
raise ValueError("No task definition selected. Exiting.") | |
return selected_task_def | |
def select_container(task_definition): | |
"""Select a container from the task definition.""" | |
# Get the task definition details | |
response = ecs_client.describe_task_definition(taskDefinition=task_definition) | |
containers = response["taskDefinition"]["containerDefinitions"] | |
container_names = [container["name"] for container in containers] | |
selected_container = pzp(candidates=container_names, lazy=True) | |
if not selected_container: | |
raise ValueError("No container selected. Exiting.") | |
# Find the selected container | |
for container in containers: | |
if container["name"] == selected_container: | |
return container | |
def main(): | |
"""Main function to interactively select a task definition and container.""" | |
try: | |
task_definition = select_task_definition() | |
container = select_container(task_definition) | |
print_container_details(container) | |
except Exception as e: | |
print(f"Error: {e}", file=stderr) | |
exit(1) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment