1

In my stack definition I pull in a number of parameters from SSM Parameter Store...

const p1 = ssm.StringParameter.fromStringParameterAttributes( ... )
const p2 = ssm.StringParameter.fromStringParameterAttributes( ... )

I then pass them along to the relevant lambdas as environment vars...

environment: {
    PARAM_ONE: p1.stringValue
    PARAM_TWO: p2.stringValue
}

However I don't want all of those parameters to be mandatory. I would like the ones that exist to be passed in as env vars, and the ones that don't to just remain undefined as my app has defaults for them anyway. However, trying to inspect the value of p1.stringValue just gives me a Token, not a value, so I can't do any logic based on it's presence or absence: https://docs.aws.amazon.com/cdk/latest/guide/tokens.html

If I ask for the parameter and it is not defined in SSM Parameter Store I then get an error that I can't catch or ignore when it tries to build the changeset and the deployment fails...

MyApp: creating CloudFormation changeset...

❌  MyAppStack failed: Error [ValidationError]: Unable to fetch parameters [/myapp/param1,/myapp/param2] from parameter store for this account.

So how can I deal with SSM parameters which may or may not exist at deploy time?

Roger Heathcote
  • 3,091
  • 1
  • 33
  • 39
  • What you're asking is against AWS best practices - your CDK app should synth to the same CloudFormation template in every environment. You shouldn't have any tests based on the existing environment in your CDK code. – gshpychka Nov 03 '21 at 06:18

1 Answers1

0

I assume you are only grabbing the manager in your import, not the actual values inside your secrets. If this is the case, then your best bet is to leverage the SDK to do this for you - a simple call using the SDK (which will be run during the synth stage of a cdk deploy or cdk synth) to see if said SMM fields/groups exist. If they do, go ahead and import them.

I do something very similar with Layers - the from methods for layers require the version number - that may change at any time. So i have a small function that gets the latest version number of a given layer using the SDK and i can then use that to import the layer definition into my stack.

If you are trying to get the actual secret inside the secret manager parameter ... that is better suited to outside the CDK for most scenarios - done in the exact location you need the secrets so you dont end up with secret value in plain text somewhere.

lynkfox
  • 2,003
  • 1
  • 8
  • 16
  • This goes against best practices - you CDK app should be deterministic and should not make network calls during synth. – gshpychka Nov 03 '21 at 06:17
  • Thanks. I'm not using this for secrets, only config params, secrets I access directly at runtime. I'm also pretty new to this, I think I know how to use sdk to check for a param, but I don't suppose you could be a bit more explicit about where that code should go. I'm a little confused by the "run during the synth stage " bit. Do I just wrap my .fromStringParameterAttributes() calls in my stack definition in an sdk call that checks for them? And if so that's great, thanks! – Roger Heathcote Nov 03 '21 at 08:44
  • @gshpychka My reading is that it's something to consider carefully but not absolutely forbidden, but do you have a better way to accomplish what I'm trying to do? Something that better adheres to best practices? – Roger Heathcote Nov 03 '21 at 08:50
  • @RogerHeathcote you can pass the names of the parameters to the lambda instead, and read the values with SDK from within the lambda. You would need to give the lambda role proper access first. – gshpychka Nov 03 '21 at 10:15
  • @gshpychka - You litterally cannot import a layer that already exists into a new CDK stack without the version number. To avoid this you would have to hard code the version number. Best Practices are all good but there are situations like this where their are parts you need that are outside your control that you must perform in such a manner. Using the SDK is exactly what bases level constucts do anyways, and its entirely acceptable practice to do so yourself. – lynkfox Nov 03 '21 at 12:23
  • Not sure what a layer has to do with this. All SDK calls should be cached to the context and commited to VCS, and not repeated on subsequent synths. That's what base level constructs do - there should be no network calls during synth. I've provided a solution that achieves it in my comment. – gshpychka Nov 03 '21 at 12:33
  • im providing examples that *have* to be done at synth time because the base level construct cannot, in contrary to best practices. Another example is Dynamo Event Streams. If you attempt to importa table not under your stacks control and make use of its dynamo stream, you must use the SDK to retrieve that stream because the arn for that stream changes and the constructs cannot retrieve it -- This is even noted in this Issue on the CDK that it is not possible to do by default: https://github.com/aws/aws-cdk/issues/7470#issuecomment-627332222 – lynkfox Nov 03 '21 at 16:31
  • best practices are important! But real world scenarios do not always fall into line with those. There are many real world scenarios that mean you cannot control the resource in the same stack (or a linked stack ) and you have no choice for the time being to import it. This often leads to properties that cannot be imported and must be discovered at time of synth. Is it ideal? no. Does it answer the question at hand for the given scenario? Yes. – lynkfox Nov 03 '21 at 16:33
  • This is all a little above my pay grade but here's my take so far. Using network calls at synth time might means your stack is not determined entirely by code. I understand how that could be very bad in some cases. However in my case, should an sdk call fail I can detect it and abort the build so I will only ever have deliberately chosen values. And even if I didn't do that the worst that could happen is a lambda falls back to the sensible defaults I have set for it. So I don't see that it's a biggie. Please correct me if I'm wrong though, I am pretty new to this. – Roger Heathcote Nov 04 '21 at 15:12
  • I see you as correct @RogerHeathcote - in real world situations these are the things we have to deal with, especially when migrating to new systems/architectures/platforms. If you can avoid it? It would be for the best. Doing it this way just because the best way is too costly (Time and or money) is not a great solution, but can be a stepping stone as long as its not a final solution. – lynkfox Nov 04 '21 at 16:20
  • I'm now a little stuck on how to make async calls using aws-sdk before defining the stack class in cdk. Seems you can't use async calls in a constructor which is fair enough. Did you encounter similar issues @lynkfox? My best guess so far is some sort of factory function. – Roger Heathcote Nov 09 '21 at 20:44
  • im not sure - in python you dont have to do async sdk calls at all, and they just fire in sequence during the constructor of a given stack. I am unfamiliar as to why the Typescript SDK needs everything to be async. – lynkfox Nov 10 '21 at 14:27