<@U01MV4U2EV9> I just took the <parameter-prototyp...
# general
b
@thdxr I just took the parameter-prototype for a spin and it so much cleaner than having to make SSM calls directly in your lambda functions. This should definitely become part of SST core šŸ™
a
What is this?
I’m using SSM with Middy, to consume them in run-time.
t
sweet! yeah we're planning on integrating it soon, need to figure out a few things
it unlocks a lot of cool things
a
Is this to avoid using env-vars @thdxr?
t
@AdriƔn Mouly this is me playing around with adding
sst.Parameter
yeah
a
You mentioned that to me before.
I see.
Yeah. I would like to read more about this.
b
nice! The only thing I'm not grokking is the fallback. How does that work?
t
yeah that's one of the things we need to think about. Basically it'll first search
/<your-app>/<your-stage>/MY_CONFIG_VALUE
if it's not found there it'll go to
/<your-app>/fallback/MY_CONFIG_VALUE
This is useful if you have multiple environments in the same AWS account and you don't want to set STRIPE_API_KEY 10 times
a
I like this, also have the same requirement.
t
we're still figuring out the api design for this, might allow you to specify a chain of places to look
b
that makes a lot of sense
a
When is this planned to be worked on?
t
now that the conf is over I'm shifting over to completing the graphql stack - this Parameter concept is going to be part of that
b
yes!
a
Nice!
t
so it'll be my focus for the next month or so, hopefully we can release parameter independently
we're also going to let you set secret parameters in the console, so you don't have to go into aws console
a
Awesome.
b
SST is becoming the best thing since sliced bread
a
Same to me, haha.
b
LOL
@AdriƔn Mouly here's an example I got working Create the following file
stacks/Parameter.ts
Copy code
import { App, Function as Fn } from "@serverless-stack/resources";
import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
import { StringParameter } from "aws-cdk-lib/aws-ssm";
import { Construct } from "constructs";
import fs from "fs";

export class Parameter extends Construct {
  public readonly name: string;
  private static readonly all = new Set<string>();

  constructor(scope: Construct, name: string, value?: string) {
    super(scope, name);
    const app = App.of(scope) as App;
    Parameter.all.add(name);
    this.name = name;

    if (value) {
      new StringParameter(this, name, {
        parameterName: `/${app.name}/${app.stage}/${name}`,
        stringValue: value,
      });
    }
  }

  public static Secret = Symbol();

  public static create<T extends Record<string, string | typeof Parameter.Secret>>(
    scope: Construct,
    params: T
  ) {
    const result: Record<string, Parameter> = {};
    for (const [key, value] of Object.entries(params)) {
      result[key] = new Parameter(scope, key, typeof value === "string" ? value : undefined);
    }

    return result as Record<keyof T, Parameter>;
  }

  public static use(func: Fn, ...params: Parameter[]) {
    const values = params.map((p) => p.name).join(",");
    const app = App.of(params[0]) as App;
    const policy = new PolicyStatement({
      resources: params.flatMap((p) => [
        `arn:aws:ssm:${app.region}:${app.account}:parameter/${app.name}/${app.stage}/${p.name}`,
        `arn:aws:ssm:${app.region}:${app.account}:parameter/${app.name}/fallback/${p.name}`,
      ]),
      actions: ["*"],
      effect: Effect.ALLOW,
    });
    func.addToRolePolicy(policy);
    func.addEnvironment("SSM_VALUES", values);
    func.addEnvironment("SSM_PREFIX", `/${app.name}/${app.stage}/`);
  }

  public static codegen() {
    fs.mkdirSync("node_modules/@types/sst-parameters", {
      recursive: true,
    });
    fs.writeFileSync(
      "node_modules/@types/sst-parameters/package.json",
      JSON.stringify({
        types: "index.d.ts",
      })
    );
    fs.writeFileSync(
      "node_modules/@types/sst-parameters/index.d.ts",
      `
     import "@serverless-stack/node/config";
     declare module "@serverless-stack/node/config" {
      export interface ConfigType {
        ${[...Parameter.all].map((p) => `${p}: string`).join(",\n")}
      }
    }`
    );
  }
}
Then, in
stacks/Api.ts
I have the following route:
Copy code
...
"GET /config": {
  function: {
    handler: "functions/config.handler",
  },
},
...
here's how I create a secret parameter:
Copy code
const parameter = Parameter.create(stack, {
  HUBSPOT_APIKEY: Parameter.Secret,
});
note: since this is a secret and you're only setting it to a Symbol, make sure you create the parameter in the Parameter Store on AWS I then get the function reference call
Parameter.use
Copy code
const createConfigFunc = api.getFunction("GET /config");
Parameter.use(createConfigFunc!, parameter.HUBSPOT_APIKEY);
My Lambda function handler looks as follows:
Copy code
import { Config } from "@serverless-stack/node/config";
import { APIGatewayProxyHandlerV2 } from "aws-lambda";

export const handler: APIGatewayProxyHandlerV2 = async (event) => {
  const body = {
    test: true,
    hubspotApiKey: Config.HUBSPOT_APIKEY,
  };
  return {
    statusCode: 200,
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body, null, "  "),
  };
};
a
Interesting.
Also, did you think about run-time parameters? or just build-time params?
For example, we pull some credentials like SQL creds in runtime from SSM.
t
nice summary! the api will improve when this is integrated and
functions
would just have a
parameters
option just like permissions
so you won't have to pull out the function to manually call Parameter.use
@AdriƔn Mouly this works for both runtime values and values known at deploy time
b
I like that. Would definitely simplify things
I'll check back in tomorrow. It's supper time in South Africa šŸ‡æšŸ‡¦ See ya
d
Can this support encrypted parameters ? It's a mess to try to do this now
t
yep it'll encrypt
a
Very interesting. Would this be applicable to a scenario where you want to, say, call endpoints defined in Stacks from other repos? Like, could you use SSM parameter store as a kind of registry of resource names or whatever? Or is there a much better approach that I'm unaware of? Currently confused about this. Configuring resource names - say APIGW resources - when you don't have a stable name or base URL or anything. (Like, SSM as a kind of DNS table in scenarios where you don't have/want to register domain name)
t
haven't thought too hard on sharing things across multiple SST apps but I imagine it can be used for that
if you create the parameter in both apps but only set it in one, it should "just work"