StateStorage¶
Serverless state and configuration storage.
Overview¶
The AWS Parameter Store (SSM) was originally designed as a place to store configuration. It turns out that it is also a pretty handy place for storing small bits of state information in between serverless executions.
StateStorage is a simple wrapper for SSM getParameter and putParameter functions, abstracting it into a contextual storage of small JSON objects.
Why use this instead of AWS SSM API directly?
- Simple Promise or async syntax
- Automatic object serialization/deserialization
- Logging
- Consistent naming convention
Injector
depends on one other utility to work:
Install¶
npm install @sailplane/state-storage @sailplane/logger @aws-sdk/client-ssm
Examples¶
Your Lambda will need permission to access the Parameter Store. Here’s an example in serverless.yml
:
provider:
name: aws
environment:
STATE_STORAGE_PREFIX: /${opt:stage}/myapp
iamRoleStatements:
- Effect: Allow
Action:
- ssm:GetParameter
- ssm:PutParameter
Resource: "arn:aws:ssm:${opt:region}:*:parameter${self:provider.environment.STATE_STORAGE_PREFIX}/*"
- Effect: Allow
Action:
- kms:Decrypt
- kms:Encrypt
Resource: "arn:aws:kms:${opt:region}:*:alias/aws/ssm"
Condition:
StringEquals:
"kms:EncryptionContext:PARAMETER_ARN": "arn:aws:ssm:${opt:region}:*:parameter${self:provider.environment.STATE_STORAGE_PREFIX}/*"
Note that this is the complete set of possible permissions.
Not all are needed if only reading parameters or if not using the secure
option.
Simple example storing state
import {StateStorage} from "@sailplane/state-storage";
const stateStore = new StateStorage(process.env.STATE_STORAGE_PREFIX!);
export async function myHandler(event, context): Promise<any> {
let state = await stateStore.get('thing', 'state');
const result = await processRequest(state, event);
await stateStore.set('thing', 'state', state);
return result;
}
See More Examples for another example.
Unit testing your services¶
Use StateStorageFake
to unit test your services that use StateStorage
. The fake will
store data in instance memory, instead of the AWS Parameter Store.
Type Declarations¶
import { SSMClient } from "@aws-sdk/client-ssm";
interface StateStorageOptions {
/** If true, do not log values. */
quiet?: boolean;
/**
* If true, store as encrypted or decrypt on get. Uses account default KMS key.
* Implies quiet as well.
*/
secure?: boolean;
/**
* If set, set and get the value as is, not JSON. (Only works for string values.)
*/
isRaw?: boolean;
}
/**
* Service for storing state of other services.
* Saved state can be fetched by any other execution of code in the AWS account, region,
* and environment (dev/prod).
*
* Suggested use with Injector:
* Injector.register(StateStorage, () => new StateStorage(process.env.STATE_STORAGE_PREFIX));
*/
export declare class StateStorage {
private readonly namePrefix;
private readonly ssm;
/**
* Construct
*
* @param namePrefix prefix string to start all parameter names with.
* Should at least include the environment (dev/prod).
* @param ssm the SSMClient to use
*/
constructor(namePrefix: string, ssm?: SSMClient);
/**
* Save state for a later run.
*
* @param {string} service name of the service (class name?) that owns the state
* @param {string} name name of the state variable to save
* @param value content to save
* @param optionsOrQuiet a StateStorageOptions, or if true sets quiet option. (For backward compatibility.)
* @returns {Promise<void>} completes upon success - rejects if lacking ssm:PutParameter permission
*/
set(service: string, name: string, value: any, optionsOrQuiet?: boolean | StateStorageOptions): Promise<void>;
/**
* Fetch last state saved.
*
* @param {string} service name of the service (class name?) that owns the state
* @param {string} name name of the state variable to fetch
* @param optionsOrQuiet a StateStorageOptions, or if true sets quiet option. (For backward compatibility.)
* @returns {Promise<any>} completes with the saved value, or reject if not found or lacking ssm:GetParameter permission
*/
get(service: string, name: string, optionsOrQuiet?: boolean | StateStorageOptions): Promise<any>;
protected generateName(service: string, name: string): string;
}
export {};
import { StateStorage } from "./state-storage";
/**
* Version of StateStorage to use in unit testing.
* This fake will store data in instance memory, instead of the AWS Parameter Store.
*/
export declare class StateStorageFake extends StateStorage {
storage: {};
constructor(namePrefix: string);
set(service: string, name: string, value: any, options: any): Promise<void>;
get(service: string, name: string, options: any): Promise<any>;
}