Skip to content

fix: Dynamic set SSM-paramater tier #4613 #4622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
28 changes: 27 additions & 1 deletion lambdas/libs/aws-ssm-util/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
});
});
});
7 changes: 7 additions & 0 deletions lambdas/libs/aws-ssm-util/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,26 @@ export async function getParameter(parameter_name: string): Promise<string> {
return result;
}

export const SSM_ADVANCED_TIER_THRESHOLD = 4000;

export async function putParameter(
parameter_name: string,
parameter_value: string,
secure: boolean,
options: { tags?: Tag[] } = {},
): Promise<void> {
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',
}),
);
}