diff --git a/docs/configuration.md b/docs/configuration.md index bcf6d13aa1..98d3db6653 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -19,6 +19,12 @@ To be able to support a number of use-cases, the module has quite a lot of confi The module uses the AWS System Manager Parameter Store to store configuration for the runners, as well as registration tokens and secrets for the Lambdas. Paths for the parameters can be configured via the variable `ssm_paths`. The location of the configuration parameters is retrieved by the runners via the instance tag `ghr:ssm_config_path`. The following default paths will be used. Tokens or JIT config stored in the token path will be deleted after retrieval by instance, data not deleted after a day will be deleted by a SSM housekeeper lambda. +Furthermore, to accommodate larger JIT configurations or other stored values, the module implements automatic tier selection for SSM parameters: + +- **Parameter Tiering**: If the size of a parameter's value exceeds 4KB (specifically, 4000 bytes), the module will automatically use the 'Advanced' tier for that SSM parameter. Values smaller than this threshold will use the 'Standard' tier. +- **Cost Implications**: While the 'Standard' tier is generally free for a certain number of parameters and operations, the 'Advanced' tier incurs costs. These costs are typically pro-rated per hour for each parameter stored using the Advanced tier. For detailed and up-to-date pricing, please refer to the [AWS Systems Manager Pricing page](https://aws.amazon.com/systems-manager/pricing/#Parameter_Store). +- **Housekeeping Recommendation**: The last sentence of the "AWS SSM Parameters" section already mentions that "data not deleted after a day will be deleted by a SSM housekeeper lambda." It is crucial to ensure this or a similar housekeeping mechanism is active and correctly configured, especially considering the potential costs associated with 'Advanced' tier parameters. This utility should identify and delete any orphaned parameters to help manage costs and maintain a clean SSM environment. + | Path | Description | | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ssm_paths.root/var.prefix?/app/` | App secrets used by Lambda's | diff --git a/lambdas/libs/aws-ssm-util/src/index.test.ts b/lambdas/libs/aws-ssm-util/src/index.test.ts index 17c8424cf4..52e4242fdb 100644 --- a/lambdas/libs/aws-ssm-util/src/index.test.ts +++ b/lambdas/libs/aws-ssm-util/src/index.test.ts @@ -9,7 +9,7 @@ import 'aws-sdk-client-mock-jest/vitest'; import { mockClient } from 'aws-sdk-client-mock'; import nock from 'nock'; -import { getParameter, putParameter } from '.'; +import { getParameter, putParameter, SSM_ADVANCED_TIER_THRESHOLD } from '.'; import { describe, it, expect, beforeEach, vi } from 'vitest'; const mockSSMClient = mockClient(SSMClient); @@ -139,4 +139,30 @@ describe('Test getParameter and putParameter', () => { // Act await expect(getParameter(parameterName)).rejects.toThrow(`Parameter ${parameterName} not found`); }); + + it.each([ + ['a'.repeat(SSM_ADVANCED_TIER_THRESHOLD - 1), 'Standard'], + ['a'.repeat(SSM_ADVANCED_TIER_THRESHOLD), 'Advanced'], + ['a'.repeat(SSM_ADVANCED_TIER_THRESHOLD + 1), 'Advanced'], + ])('Puts parameters with value and sets correct SSM tier based on size and threshold', async (data, expectedTier) => { + // Arrange + const parameterValue = data; + const parameterName = 'testParamSmall'; + const secure = false; + const output: PutParameterCommandOutput = { + $metadata: { httpStatusCode: 200 }, + }; + mockSSMClient.on(PutParameterCommand).resolves(output); + + // Act + await putParameter(parameterName, parameterValue, secure); + + // Assert + expect(mockSSMClient).toHaveReceivedCommandWith(PutParameterCommand, { + Name: parameterName, + Value: parameterValue, + Type: 'String', + Tier: expectedTier, + }); + }); }); diff --git a/lambdas/libs/aws-ssm-util/src/index.ts b/lambdas/libs/aws-ssm-util/src/index.ts index a2d842b628..0b4925c17d 100644 --- a/lambdas/libs/aws-ssm-util/src/index.ts +++ b/lambdas/libs/aws-ssm-util/src/index.ts @@ -17,6 +17,8 @@ export async function getParameter(parameter_name: string): Promise { return result; } +export const SSM_ADVANCED_TIER_THRESHOLD = 4000; + export async function putParameter( parameter_name: string, parameter_value: string, @@ -24,12 +26,17 @@ export async function putParameter( options: { tags?: Tag[] } = {}, ): Promise { const client = getTracedAWSV3Client(new SSMClient({ region: process.env.AWS_REGION })); + + // Determine tier based on parameter_value size + const valueSizeBytes = Buffer.byteLength(parameter_value, 'utf8'); + await client.send( new PutParameterCommand({ Name: parameter_name, Value: parameter_value, Type: secure ? 'SecureString' : 'String', Tags: options.tags, + Tier: valueSizeBytes >= SSM_ADVANCED_TIER_THRESHOLD ? 'Advanced' : 'Standard', }), ); }