diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 81df390..dabe46b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,10 +11,12 @@ jobs: strategy: matrix: python: - - "3.7" - "3.8" - "3.9" - "3.10" + - "3.11" + - "3.12" + - "3.13" steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python }} @@ -23,7 +25,7 @@ jobs: python-version: ${{ matrix.python }} - name: Cache python packages - uses: actions/cache@v2 + uses: actions/cache@v4 env: cache-name: test-python-packages with: diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c182e37 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +env +*.egg-info diff --git a/README.md b/README.md index 701f6b5..8e9084f 100644 --- a/README.md +++ b/README.md @@ -192,3 +192,10 @@ Secrets are represented in yaml as SSM name and value pairs. They're uploaded to ```bash ecsctrl secrets store -e production.env secrets.yaml ``` + +Dump secrets from SSM parameter store. You can optionally filter params by name using regexp. +--- + +```bash +ecsctrl secrets dump -e production.env --filter "db_.*" secrets.yaml +``` diff --git a/ecsctrl/cli.py b/ecsctrl/cli.py index a887f6d..f12634e 100644 --- a/ecsctrl/cli.py +++ b/ecsctrl/cli.py @@ -309,11 +309,18 @@ def store( @secrets.command() @click.argument("spec-file", type=str) +@click.option( + "--filter", + type=str, + default=None, + help="Export only secrets matching given regexp pattern", +) @common_options @click.pass_context def dump( ctx, spec_file, + filter, env_file, json_file, var, @@ -323,7 +330,7 @@ def dump( vars = VarsLoader(env_file, var, json_file, sys_env).load() var_lut = generate_var_lut(vars) ssm = BotoClient("ssm", dry_run=ctx.obj["boto_client"].dry_run) - secrets = dump_secrets(ssm) + secrets = dump_secrets(ssm, filter) render_dumped_secrets(click, secrets, var_lut, spec_file) diff --git a/ecsctrl/dump/secrets.py b/ecsctrl/dump/secrets.py index f5e1561..6a21239 100644 --- a/ecsctrl/dump/secrets.py +++ b/ecsctrl/dump/secrets.py @@ -1,3 +1,4 @@ +import re from . import substitute_with_expressions @@ -17,7 +18,7 @@ def list_secrets(ssm): yield parameter -def dump_secrets(ssm): +def dump_secrets(ssm, filter=None): for parameter in list_secrets(ssm): parameter_name = parameter["Name"] response = ssm.call( @@ -26,7 +27,8 @@ def dump_secrets(ssm): WithDecryption=True, ) - yield parameter_name, response["Parameter"]["Value"] + if filter is None or re.match(filter, parameter_name): + yield parameter_name, response["Parameter"]["Value"] def render_dumped_secrets(click, secrets, vars_lut, target_file):