- Maintain dynamic configuration. Using environment variable in lambda is in-flexible and hard to maintain.
- Read/Write/Delete configurations across lambda functions. Similar to what you did locally:
var config = require('config');
var id = config.get('id');
- Save you time for creating dynamoDB table, writing database I/O functions for every project.
- Internal cache mechanism reduce redundant database access, hence lower dynamoDB capacity and lower cost.
- A standardized way to access/manage/store configuration.
- A trade-off solution between performance and running cost (propagation delay & access time & cheap lambda execution cost vs high dynamoDB capacity vs running cost of professional configuration system)
- You need to accept a period of propagation delay after updating a config IF you use the cache mechanism to save dynamoDB access cost (default expire time 300s)
You may skip this part if you already have and would like to keep using that access key/secret key.
- Prepare an AWS account (Free Tier SHOULD cover this service's costs, ref: AWS Free)
- [Optional] Create a machine user at AWS IAM. Create User -> Tick Programmatic access -> Attach existing policies directly ->
2.a Choose the AdministratorAccess or
2.b Create Policy -> JSON -> Copy the Policy Sample at the end of this readme -> Review Policy -> Create Policy - Prepare an set of credential (1 access key + 1 secret key), either from step 2 or create a key for your user. (ref: AWS - Managing Access Keys for Your AWS Account)
- Set up the aws-cli credential environment (ref: AWS Configuration and Credential Files).
- [Optional] Test your credentials setting with aws-cli (ref: AWS STS - get-caller-identity)
aws sts get-caller-identity
You are expected to see the result:
{
"Account": "123456789012",
"UserId": "AR#####:#####",
"Arn": "arn:aws:sts::123456789012:assumed-role/role-name/role-session-name"
}
You may skip the core if you sure you DO NOT want to use the cache mechanism. E.g., you just want a dynamoDB wrapper to access your configurations.
- Install the configuration core: aws-lambda-configuration-core
- [optional, for testing only] Go to your AWS DynamoDB Console. Find your configuration table. Create a new item:
{
"configName": "settings",
"data": {
"sampleConfig": "sampleValue"
}
}
You may skip this part if you DO NOT want to use encryption related functions.
-
Go To AWS IAM -> Encryption keys -> Choose the region -> Create Key -> Input Alias (recommend: lambda-configuration-key) and Choose KMS for Key Material Origin -> Next Step x N -> Finish.
-
Choose your created key -> Under Key Policy/Key Users -> Add ALL lambda execution roles that will use the encryption functions -> Save Changes
aws-lambda-configuration use DynamoDB as storage. If you installed the core, there will be a dynamoDB table for you. If not, you need to prepare at least one table according to the following standard.
- The table name (default: lambda-configurations) and item name (default: settings) is arbitrary and configurable.
- The only requirement is the table MUST use a Primary partition key named configName (String).
- Each config has the format:
{
"configName": string,
"data": any(prefer object),
}
There are few ways for you to interact with your configurations.
-
Manually access via AWS Console
Go to AWS DynamoDB Console, find your table and directly access your config. -
Programmatic access via Core
aws-lambda-configuration-core comes with a lambda function (default: lambda-configuration). You can directly invoke the lambda function. Detail usage refer to aws-lambda-configuration-core. -
Programmatic access via library
Install the library/module to your serverless project.
JavaScript (TypeScript): aws-lambda-configuration-js
Python: In future
- Cache vs Core vs Direct:
- Use cache mechanism, get({ mode: 'cache' }), for config that are [relatively stable & frequently get & allow short period of fault tolerant], e.g. global server settings, cors settings, 3 parties credentials settings, etc.
- Use core, get({ mode: 'core' }), if you want to isolate permission, e.g. your lambda execution role only need
lambda:InvokeFunction
but notdynamo:*
. - Otherwise, use direct way, get().
- Table Naming:
- Use tableName separates development stages, e.g.
lambda-configurations-dev
,lambda-configurations-stage
,lambda-configurations-prod
- Use tableName separates projects, e.g.
projectA-configurations-dev
,projectB-configurations-prod
So that your project's settings will not be accidentally modified by some newer of this library using the same AWS account
- Use tableName separates development stages, e.g.
- Document Naming:
- Use standard documentName, e.g.
settings
for normal server settings (ids, name, domain, etc.) andsecrets
for sensitive settings (tokens, server credentials, keys, etc.) - Use 1 document for 1 unit (user/device/account/etc), i.e. use documentName separates users' settings, e.g. a uuid/v4 string. Make sure you don't have a user called
settings
orsecrets
- Use standard documentName, e.g.
- Read/Write Configuration:
- If you need to read/write only 1 config, set
key: PATH_TO_SUB_OBJECT
to reduce transfer payload size. - If you need to read/write multiple config, leave
key: undefined
to get/set the whole document to reduce multiple invoke times. - Read static config outside the API handler, e.g. initializer, global variable, etc. to reduce repeated lambda invoke.
- If you need to read/write only 1 config, set
- Security:
- Encrypt the sensitive data using library's functions: encrypt/encryptKEK.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"dynamodb:*",
"cloudwatch:*",
"cloudformation:*",
"iam:*",
"lambda:*",
"kms:*"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
Memory Usage
37MB + amount of cache
Time (for getting a string config: "sampleValue")
Cold Start + No cache: ~2700ms
Warm + Without cache: 400ms~1200ms
With cache: 50ms~200ms
With cache & Very frequent access: 40ms~80ms
Direct access from library: 10ms~70ms