SAM template environment variables use map, list, associative array? - amazon-web-services

I've only seen examples with single values in SAM templates:
Environment:
Variables:
TABLE_NAME: my-table
I want to do something like this but doesn't seem to work:
Environment:
Variables:
myVar:
- prop1: aaa
prop2: sdfsdfsd
prop3: ssss
- prop1: bbb
prop2: wwwwww
prop3: aaaaa
I want to have an environment variable that is like a list of objects. I could store a delimited string and parse it myself but I'd prefer to have it be like an object/map/list like if I'm ready a YAML file.

The closest you can do is to json encode the value for your environmental variable
and decode it using the runtime language:
Environment:
Variables:
USER: '{"name": "john", "surname": "galt"}'
If you want to prevent decoding json on each request, move your decoding logic outside the handler, in this case code won't be re-executed while lambda is hot.
Any declarations in your Lambda function code (outside the handler code, see Programming Model) remains initialized, providing additional optimization when the function is invoked again. For example, if your Lambda function establishes a database connection, instead of reestablishing the connection, the original connection is used in subsequent invocations. We suggest adding logic in your code to check if a connection exists before creating one.
Read about lambda execution model
I personally would create a json file, store it in s3 bucket and use an environment variable to specify s3 url to that file. Additionally, use the same technique I mentioned above or use even more complicated caching mechanism depending on the situation when retrieving the config file

Related

Where do I tell AWS SAM which file to chose depending on the stage/environment?

In the app.js, I want to require a different "config" file depending on the stage/account.
for example:
dev account: const config = require("config-dev.json")
prod account: const config = require("config-prod.json")
At first I tried passing it using build --container-env-var-file but after getting undefined when using process.env.myVar, I think that env file is used at the build stage and has nothing to do with my function, but I could use it in the template creation stage..
So I'm looking now at deploy and there are a few different things that seem relevant, but it's quite confusing to chose which one is relevant for my use case.
There is the config file, in which case, I have no idea how to configure it since I'm in a pipeline context, so where would I instruct my process to use the correct json?
There is also parameters, and mapping.
My json is not just a few vars. its a bit of a complex object. nothing crazy not simple enough to pass the vars 1 by 1.
So I thought a single one containing the filename that I want to use could do the job
But I have no idea how to tell which stage of deployment I currently am in, or how to pass that value to access it from the lambda function.
I also faced this issue while exectuing aws lambda function locally.By this command my issue was solved.
try to configure your file using the sam build command

How can I reuse a parameterized Postman test across endpoints?

In Postman, I can create a set of common tests that run after every endpoint in the collection/folder Tests tab, like so:
pm.test("status code is 200", function () {
pm.response.to.have.status(200);
});
But how should I do this for my schema validation on the response object? Each endpoint has a different expected schema. So I have something like this on each individual endpoint:
const schema = { type: 'array', items: ... }
pm.test('response has correct schema', function () {
const {data} = pm.response.json();
pm.expect(tv4.validate(data, schema)).to.be.true;
});
I can't extract this up to the collection level because each schema is different.
Now I find that I want to tweak that test a little bit, but I'll have to copy-and-paste it into 50 endpoints.
What's the recommended pattern here for sharing a test across endpoints?
I had the same issue some years ago, and I found two ways to solve this:
Create a folder for each structure object
You can group all your test cases that share the same structure into a new folder and create a test case for this group. The issue with this is that you will be repeating the requests in other folders. (For this solution, you will need to put your "tests cases" into the folder level)
Create a validation using regular expressions (recommended)
Specify in the name of each request a set of rules (these rules will indicate what kind of structure or call they might have). Then you create a validation in the first parent folder for each type of variation (using regex). (You will need to create documentation and some if statements in your parent folder)
E.g.: [POST] CRLTHM Create a group of homes
Where each initial is meaning to:
CR: Response must be 201
LT: The response must be a list of items
HM: The type of the response must be a home object
And the regex conditional must be something like this (this is an example, please try to make your regex accurate):
if(/CRLTHM\s/.test(pm.info.requestName))
(In this image, NA is referring just to Not Authenticated)

Storing parameterized values in cloud formation and referencing it

Is there a way to store variables in Cloudformation?
I've created a resource with a name which is a stage specific name in the following form:
DeliveryStreamName: {'Fn::Sub': ['firehose-events-${Stage}', 'Stage': {'Ref' : 'Stage' }]}
Now if I've to create a cloudwatch alarm on that resource I'm again following the same pattern:
Dimensions:
- Value: {'Fn::Sub': ['firehose-events-${Stage}', 'Stage': {'Ref' : 'Stage' }]}
Instead if I could store the whole value in one variable, it would be much easier for me to refer it.
I thought initially storing it in parameters, like this:
Parameters:
FirehoseEvent: {Type:String, Default: 'firehose-events-${Stage}'}
But the stage value doesn't seem to get passed in here. And there is no non default value either for this resource name.
The other option I considered was using mapping, but that defeats the purpose of using ${Stage}.
Is there some other way which I've missed?
Sadly you haven't missed anything. Parameters can't reference other parameters in their definition.
The only way I can think of doing what you which would be through a custom macro. In its simplest form the macro would just perform traditional find-and-replace type of template processing.
However, the time required to develop such macro could be not worth its benefits, at least in this simple example you've provided in the question.

In AWS Resolver Mapping Template, is there any method for removing a field from an object?

I'm writing code for my GraphQL resolvers in AWS AppSync with resolver mapping template.
I know that there is a put mehtod that I can use for add a field to input object or any other object. Like this (for example):
$util.qr($name.put("firstName", "$ctx.args.input.firstName"))
But now I want to remove a field from an object, for example, the input object.
Is there any mehtod similar to the put method but for removing a field. something like:
$util.qr($ctx.args.input.remove("firstName"))
I am new to AWS and DynamoDB and AppSync.( you can consider me as an absolute beginner. )
Use foreach and make a new array.
#set($newInput={})
#foreach ($key in $ctx.args.input.keySet())
#if($key!="firstName")
$util.qr($newInput.put($key, $ctx.args.input.get($key)))
#end
#end
Yes, generally you can use $myObject.remove("myKey") on objects that you create in a mapping template, however, I will add the disclaimer that this will not always work on objects in the $ctx as some parts are immutable. AppSync bundles utility methods that make dealing with objects in mapping templates easier (e.g. making copies of objects). This functionality is actually tied to that of Apache Velocity so you can read more about how it works in those docs.
In AppSync, the arguments in a query or mutation are exposed in the request mapping template as $context.args. If you have passed in an argument named input you can remove it as follows:
$util.quiet($context.args.remove("input"))
or its using the alias for quiet (identical to the above):
$util.qr($context.args.remove("input"))
This can be used in both the request and response mapping template. It can also be used to remove nested properties:
$util.qr($context.args.input.remove("nestedProp"))

Google Deployment Manager - BigTable example

I have been trying this example provided in the Google's Deployment Manager GitHub project.
It works, yet I am not sure what is the purpose of creating three instances named instance_create, instance_update and instance_delete.
For example, taken from the link:
instance_create = {
'name':
'instance_create',
'action':
'gcp-types/bigtableadmin-v2:bigtableadmin.projects.instances.create',
'properties': {
'parent': project_path,
'instanceId': instance_name,
'clusters': copy.deepcopy(initial_cluster),
'instance': context.properties['instance']
},
'metadata': {
'runtimePolicy': ['CREATE']
}
}
What is the purpose of `action` and `metadata`.`runtimePolicy`? I have tried to find it in the documentation but failed miserably.
Why there are three `BigTable` instances there?
You are right, the documentation is missing the information, which would answer your questions regarding these parameters.
However, it helps knowing what's going on in the Depoyment Manager example you linked.
First of all, the following line in the config.yaml is where the things get tricky:
resources:
- name: my-bigtable
type: bigtable.py
This line will do a call to the bigtable.py python file, which sets the resource type of the deployment to that which are in it, under the GenerateConfig function. See how this is done here.
The resources are returned as {'resources': resources} at the end of it, being the resources variable a list of templates created there.
These templates have different name identifiers, which are set by the "name" tag.
So you are not creating three different instances with the name of instance_create, instance_update and instance_delete in this file, but you are creating three templates with those names, that will later be appended to the resources list, and later returned to the config.yaml resources.type tag.
These templates then will be sequentially build and executed by the deployment manager, once the create command is used. Note that they might appear out of order, this is due not using a schema.
It's easier to see this structure in a .yaml file format, for example, built with jinja, the template you posted would be:
resources:
- action: gcp-types/bigtableadmin-v2:bigtableadmin.projects.instances.create
name: instance_create
metadata:
runtimePolicy:
- CREATE
properties:
clusters:
initial:
defaultStorageType: HDD
location: projects/<PROJECT_ID>/locations/<PROJECT_LOCATION>
serveNodes: 4
instance:
displayName: My BigTable Instance.
type: PRODUCTION
instanceId: my-instance
parent: projects/<PROJECT_ID>
Notice that the parameters under properties are the fields in the request body to bigtableadmin.projects.instances.create (which is nesting a clusters object parameters and a instance object parameters). Note that the InstanceId under properties is always the same, hence the BigTable instance, on which the templates do the calls, is always the same one.
The thing is that, not only the example you linked creates various templates to be run in the same script, but that the resource type for each template is a call to the BigTable API.
Normally the template resources are specified with the type tag, but since you are calling a resource that is directly running an API call (i.e. instead of just specifying gcp-types/bigtableadmin-v2, you are specifying bigtableadmin-v2:bigtableadmin.projects.instances.create), the action tag is used. I haven't found this difference on usage documented anywhere, but it needs to be specified like that.
You will know if you are calling an API 'endpoint' directly if the resource ends with either create/update/delete.
Finally, the I have investigated in my side, and the metadata.runtimePolicy is tied to the fact that the resource type is an API call (like in the previous point). Once again, I haven't found this documented anywhere.
However, since this is a requirement, you will always have to set the correct value in this field. It basically boils down to have metadata.runtimePolicy set to this values, depending on which type of API call you do:
create -> ['CREATE']
update -> ['UPDATE_ON_CHANGE']
delete -> ['DELETE']
Summarizing:
You are not creating three different instances, but three different templates, which do the work on the same BigTable instance.
You need to change the resource type flag to action if you are calling an API endpoint (create/update/delete), instead of just naming the base API.
The metadata.runtimePolicy value is a requirement when doing a call to one of the aforenamed endpoints.