Skip to content

v4 breaks offline dev for services that utilize SSM #423

Description

@kuritz

Summary

On osls 4.0.0 there is no way to resolve ${ssm:} during local development without live AWS credentials, the community workaround that used to provide one has silently stopped working.

serverless-offline-ssm made credential-free local development work by monkeypatching the AWS provider's generic request() method, which the built-in ssm variable source called.

The v4 upgrade guide documents the removal of provider.request() and points at the osls-aws-provider-request-shim community project for plugins broken by it, but unfortunately that shim does not help here.

The ask is to introduce a small first-party way to resolve ${ssm:} from a local map so existing serverless projects that rely on offline dev can continue to function. I am happy to contribute it.

Reproduction

https://github.com/farmersdog/ssm-repro./run.sh from a clean clone installs pinned versions, runs four independent cases, and prints a summary table:

  1. osls 3.76.1 — the mock map applies, ${ssm:} resolves, exit 0.
  2. osls 4.0.0 — byte-identical config and plugin list, exit 1 with the error above, banner still printing.
  3. osls-aws-provider-request-shim@1.0.1 — byte-identical to case 2 in both plugin orderings. A probe subclass shows why: the shim's constructor runs, but its initialize hook never fires, because resolution has already failed. A control config with no ${ssm:} reference logs both.
  4. Cross-stage params: — a ${ssm:} reference reachable only via params.prod fails at --stage dev. Not a v4 regression; see below.

Every case runs with the credential chain fully neutralized (all AWS_* credential and profile variables unset, HOME pointed at an empty directory, IMDS disabled) and asserts the failure is AWS provider credentials not found, not a misconfigured-profile error, so an incomplete strip can't be mistaken for a result.

Not specific to print — resolution is a hard gate ahead of every command, so invoke local, package and serverless-offline fail identically.

Root cause

  • serverless-offline-ssm@6.2.0/dist/resolver.js:23-39 patches provider.request, filtering for ('SSM', 'getParameter').
  • osls@3.76.1 called exactly that: lib/configuration/variables/sources/instance-dependent/get-ssm.js:30-38.
  • osls@4.0.0 does not. The same get-ssm.js builds the client itself (:4, :10-12, :39SSMClient / GetParameterCommand) and sends via create-cached-aws-variable-source-command-sender.js:80. The provider is consulted only for getRegion() and getAwsSdkV3Config().
  • lib/plugins/aws/provider.js:1713-1715 makes provider.request a throwing stub.

The plugin patches a method the SSM source no longer calls, the patch is never exercised, and nothing warns. Timing rules out any userland fix: resolution runs at scripts/serverless.js:649, await serverless.run() — the earliest a lifecycle hook can fire — only at :687.

What I'd like

Preferred: an offline mock map consulted before the SDK call in get-ssm.js. A custom block or an env var pointing at a JSON file of parameter path → value; on a hit, return it and never construct the client.

custom:
  ssmOffline:
    '/dev/demo/api-token': mock-value

or SLS_SSM_OFFLINE_FILE=./ssm.local.json.

There some decisions needed on whether misses fall through to real SSM or fail closed, whether the env-var form should be the only one to keep it out of the config schema, and whether an explicit opt-in flag should be required so a stray map file can't affect a deploy.

Environment

osls@4.0.0 vs osls@3.76.1; serverless-offline-ssm@6.2.0; osls-aws-provider-request-shim@1.0.1 (tested, does not apply); node 22.22.2; Linux; no AWS credentials of any kind.

Thanks for maintaining this fork and considering this change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions