How to add item to YML collection conditionally using Serverless framework? - yaml

Given I have the following defined in a YML file for a resource:
Type: AWS::Cognito::UserPoolClient
CallbackURLs:
- "my-first-url"
How can I add a second item when only certain conditions are met? Unfortuantely it is not possible to simply set it to an empty string or null as it fails deployment validation. E.G something like:
Type: AWS::Cognito::UserPoolClient
CallbackURLs:
- "my-first-url"
- myCondition ? "" : undefined // Omit item
Is this possible in any way at all? Happy to use Plugin solutions etc.

You can use a CloudFormation condition like Fn::If to conditionally create stack resources. The CloudFormation documentation about conditions has all the details but somehting like this should get you started:
resources:
Conditions:
my_condition: !Equals [value_1, value_2]
Resources:
MyUserPool:
Type: AWS::Cognito::UserPoolClient
CallbackURLs:
- "my-first-url"
- !If [my_condition, "...", !Ref "AWS::NoValue"]
Replace the content of my_condition with your condition. It is referenced later in the Fn::If (the example uses the shorthand for Fn::If).
The AWS::NoValue is a pseudo parameter which can be used as a return value to remove the corresponding property. It should work here to remove the list item but I'm not sure about it, you'll need to test.

Related

How to optionally apply environment configuration?

I want to optionally apply a VPC configuration based on whether an environment variable is set.
Something like this:
custom:
vpc:
securityGroupIds:
- ...
subnetIds:
- ...
functions:
main:
...
vpc: !If
- ${env:USE_VPC}
- ${self:custom.vpc}
- ~
I'd also like to do similar for alerts (optionally add emails to receive alerts) and other fields too.
How can this be done?
I've tried the above configuration and a variety of others but just receive various different errors
For example:
Configuration error:
at 'functions.main.vpc': must have required property 'securityGroupIds'
at 'functions.main.vpc': must have required property 'subnetIds'
at 'functions.main.vpc': unrecognized property 'Fn::If'
Currently, the best way to achieve such behavior is to use JS/TS-based configuration instead of YAML. With TS/JS, you get full power of a programming language to shape your configuration however you want, including use of such conditional checks to exclude certain parts of the configuration. It's not documented too well, but you can use this as a starting point: https://github.com/serverless/examples/tree/v3/legacy/aws-nodejs-typescript
In general, you can do whatever you want, as long as you export a valid object (or a promise that resolves to a valid object) with serverless configuration.

Add tag to a map of tags where the tag key is dymamic

In a Cloudformation template, is it possible to use functions in to construct tag keys?
For some reason, tags for the AWS::EKS::Nodegroup type are different to every other resource (that I have had to use), so rather than tags being formatted like this...
Tags:
- Key: TagKey
- Value: TagValue
...they have to be formatted like this...
Tags:
TagKey: TagValue
That's all well and good if TagKey is static, but when it's value depends on the value of an incoming parameter, I'm not sure how to add the tag.
I've tried the obvious, hoping that !Sub would be run against any key names with ${}, but that fails -
Tags:
k8s.io/cluster-autoscaler/${ClusterName}: owned
null (Service: AmazonEKS; Status Code: 400; Error Code: BadRequestException; Request ID: 4bc0257f-6fed-40e2-905e-7e4cda022313; Proxy: null)
Is there a way to do this with CloudFormation?
Sadly its not possible. You would have to create your own macro in cloudformation for such a functionality.

How to assign values to my CloudFormation template written in YAML while deploying

I have written a CloudFormation template in YAML to deploy my AWS Lambda functions. I have to deploy multiple Lambda function and I want to be able to changes the key-value pair at run-time, so that I don't have to copy the entire thing again and again to make changes before deployment.
I was reading about the set builtin of Linux, but didn't find it of much help
NameOfMyLambda:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub '#My_Function_Name'
Handler: app.lambda_handler
Runtime: python3.7
MemorySize: 256
Role: !GetAtt MyExecutionRole.Arn
CodeUri: 'path/to/my/python/file'
In the above given code, I want to be able to change the "FunctionName" and "Role" at time of deployment.
Your exact use case isn't completely clear (how are you running your cloudformation?), but it sounds like you want to use CloudFormation Parameters possibly combined with Nested stacks
This would allow you to reference your cloudformation template multiple times from within an outer template, passing different parameters each time.

YAML object properties

I am defining code review configuration in YAML. I need to keep the config as compact as possible without explicitly defining xml/json like name value pairs.
- group: Naming convention
severity: medium
rules:
- name: Check API naming convention
type: pattern
element: api.#name
pattern: '.*\-.*\-\d\.\d'
properties:
- exit-on-fail
- skip-and-proceed
- etc.
What I don't like here is defining the tag "properties" to add the actions. Can the actions exist at the object level?
Indentation-wise, they can exist on the object level:
- group: Naming convention
severity: medium
rules:
- name: Check API naming convention
type: pattern
element: api.#name
pattern: '.*\-.*\-\d\.\d'
properties:
- exit-on-fail
- skip-and-proceed
- etc.
This works because YAML sees - as indentation and therefore, this still creates a list as value for the properties: key.
To compactify, you can also write them inline like daggett suggested:
- group: Naming convention
severity: medium
rules:
- name: Check API naming convention
type: pattern
element: api.#name
pattern: '.*\-.*\-\d\.\d'
properties: [exit-on-fail, skip-and-proceed, etc]
Finally, you can put them into your object mapping as long as they do not share a name with any other fields:
- group: Naming convention
severity: medium
rules:
- name: Check API naming convention
type: pattern
element: api.#name
pattern: '.*\-.*\-\d\.\d'
? exit-on-fail
? skip-and-proceed
? etc.
This creates three additional key-value pairs in your object, with the three properties being the keys and the empty string (possibly a null value depending on the YAML implementation you use) being the value. If you do this, you will need to write a custom constructor to load this into a native data structure, because you need to differentiate between the object fields and the actions. How to do this, again, depends on your YAML implementation.

How to change one value in an associative array referenced by Node

So I apologize in advance if I use yaml terminology wrong I am pretty new to it.
So I have this item in a list that is an associative array and I would like to use a node to repeat it multiple times in the file but I need to change one value in a sub array in it and I don't know how to do it without overwriting the entire array.
So here is the item in the list
- &def_service
type: service
name: Remote Service
config:
machine: ''
version: '1.0.0'
apikey: VALUE_I_WANT_TO_CHANGE
and I what I've tried to do is
- <<: *def_service
config:
apikey: NEW_VALUE
but that just overwrites the entire array so config is just
{config:{apikey:NEW_VALUE}}
I would be very grateful for an answer here I am pretty stuck.
Ok so an answer that occurred to me maybe not the best answer is just to introduce another variable for the config array like this.
- &def_service
type: service
name: Remote Service
config: &service_config
machine: ''
version: '1.0.0'
apikey: VALUE_I_WANT_TO_CHANGE
so to reference it I did this
- <<: *def_service
config:
<<: *service_config
apikey: NEW_VALUE
YAML is not a programming language. It is designed for data representation, not for data transformation.
The merge key you use (<<) is not part of the YAML specification. It is part of the YAML type repository, which is outdated (since it is defined for YAML 1.1). Therefore, your question is highly dependendent on the YAML processor you use. One processor might implement it while another does not.
Since you have a specific problem, it would probably be better to write YAML tailored to your problem, and then handle it in your code (assuming that you are in charge of the code). Something like this:
- !config_base
&def_service
type: service
name: Remote Service
config: &service_config
machine: ''
version: '1.0.0'
apikey: VALUE_I_WANT_TO_CHANGE
- !config_child
base: *def_service
substitutions:
config:
apikey: NEW_VALUE
You can then write code that does the substitution inside your deserialized YAML structure.

Resources