!Join in Serverless Framework is broken - amazon-web-services

RetrieveAllSubscribersLambdaPermissionApiGateway:
Type: 'AWS::Lambda::Permission'
Properties:
FunctionName: { "Fn::GetAtt": [ RetrieveAllSubscribersLambdaFunction, Arn ] }
Action: lambda:InvokeFunction
Principal: apigateway.amazonaws.com
SourceArn:
!Join
- "/"
- - 'arn:aws:execute-api:us-west-2:12345'
- - 'other stuff'
I have this block of code where I try and get a sourceArn for this block. I realize the sourceArn is invalid in this example, but so is the viewed output from Serverless.
An error occurred: RetrieveAllSubscribersLambdaPermissionApiGateway - 1 validation error detected: Value 'arn:aws:execute-api:us-west-2:12345/aws/:execute-api:/us-west-2/:/12345/:/a5dghhjk9//*/*' at 'sourceArn' failed to satisfy constraint: Member must satisfy regular expression pattern: arn:(aws[a-zA-Z0-9-]*):([a-zA-Z0-9\-])+:([a-z]{2}((-gov)|(-iso(b?)))?-[a-z]+-\d{1})?:(\d{12})?:(.*) (Service: AWSLambda; Status Code: 400; Error Code: ValidationException; Request ID: a8db5326-dcdd-4abc-b4ae-e4fa5c03c6bd; Proxy: null).

-- Update 2022/01/16 12:49 AM
The problem was indeed with the tags. Everything can be seen on the issue. TLDR; use the full function name (e.g. 'Fn::Join').
However, I'm almost certain that the shorthand can work, there are just some conditions that need to be met first. I will not investigate, since a working solution for the original problem has been achieved and anything else would be outside the scope.
-- Original
After some back and forth between Ryan and myself. Ryan used an alternative solution involving !Sub and the serverless-cloudformation-sub-variables plugin which can be viewed here.
In short, the problem likely lied in dependencies involving tags not correctly being resolved (most likely due to misconfigurations. Still investigating and will update if the original solution can be resolved with additional plugins).
The following plugin abstracts the use of !Sub which in turn resolved the issue,
serverless-cloudformation-sub-variables

Related

AWS::EC2::SubnetRouteTableAssociation does not appear in Resource types pane of the CloudFormation Designer

In CloudFormation it's possible to create a resource of type AWS::EC2::SubnetRouteTableAssociation
As an example, here's a snippet where I've done exactly that in YAML.
Resources:
mySubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref paramSubnetId
RouteTableId: !Ref paramRouteTableId
I have to concede that in my CloudFormation journey I "got right into it" without really using the drop-and-drag aspect of the CloudFormation Designer (which the documentation calls the Resource types Pane). I now find myself in a position where I'm using the Resource types pane, and find theres no SubnetRouteTableAssociation resource.
I have considered a couple of possibilities, but they all seem unlikely.
It's somewhere else (not under EC2), and despite looking through umpteen times I can't find it.
AWS overlooked adding it.
It's been given a different name.
Some other good reason that I can't comprehend just yet.
It's expected to use another resource type that is "overloaded" to stand-in for this resource.
So in summary, can anyone shine some light on why the resource AWS::EC2::SubnetRouteTableAssociation is listed in the Resource types pane of the CloudFormation Designer ?
Many thanks in advance.
I wanted to provide an update and how I have dealt with this in case others in the future have the same question.
The simple answer appears to be that the SubnetRouteTableAssociation does not exist within the Resource Types Pane. This does not mean that you can't have one because you can still create the resource manually within the template (writing the code directly).
This behaviour does appear at first to be a little inconsisent. One might argue that an association between two entities is not an actual AWS infrastructure object in itself and therefore is not deserving of a widget in the Designer Pane.
However, there is another similar resource type that behaves slightly differently - AWS::EC2::SubnetNetworkAclAssociation
This resource also does not have an entry in the Resource Types Pane yet when you create it - it does get created within the Designer Pane, but not as a regular looking resource, but rather as a dependency (arrow). It's very easy to overlook but if you 'mouseover' the dependency arrow, the resource name appears.
So what is the difference ? Why does SubnetRouteTableAssociation not get any representation in the Designer Pane whilst SubnetNetworkAclAssociation does (even thought that representation is merely an arrow ?
My conclusion is because one requires a resource dependency (for resource creation purposes) whilst the other does not. I would prefer that every possible CFN resource is available in the Resource Pane. I hope this explanation/conclusion helps.

How dynamically change S3 path inside DefinitionBody

My code currently is working like this
DefinitionBody:
'Fn::Transform':
Name: AWS::Include
Parameters:
Location:
Fn::Sub: s3://${Bucket}/${AWS::StackName}/${File}
# Note: Bucket and File comes from Parameters in this case
Now I try to get {$File} dynamically using FindInMap
'Fn::Transform':
Name: AWS::Include
Parameters:
Location: !Sub
- s3://${value-from-parameters-section}/${AWS::StackName}/${spec}
- { spec: !FindInMap [ "Config", "type", "file"]}
But is throwing me this error:
Failed to digest functions within transform parameters, intrinsic functions in transform block must only contain parameter values or stack metadata
So, How I can achieve to dynamically change the File name inside the DefinitonBody?
You can't do this, it is not supported. AWS docs explicitly states that no intrinsic functions are supported:
Supported functions None. CloudFormation passes any intrinsic function calls included in Fn::Transform to the specified macro as literal strings.
You can re-design your function to use custom resources instead of macros. This way you can do whatever you wish.
In this case, I think the message is self-explanatory
Failed to digest functions within transform parameters, intrinsic functions in transform block must only contain parameter values or stack metadata
Only metadata or parameters are allowed, that is the reason this works
DefinitionBody:
'Fn::Transform':
Name: AWS::Include
Parameters:
Location:
Fn::Sub: s3://${Bucket}/${AWS::StackName}/${StackType}
In case somebody needs a workaround I just remove the mapping section and rename the name of the files to match the type I want, something like this
Parameters:
StackType:
Type: String
AllowedValues:
- typeA
- typeB
Bucket:
Type: String
Then I just use this sub to get the DefinitionBody like this:
DefinitionBody:
'Fn::Transform':
Name: AWS::Include
Parameters:
Location:
Fn::Sub: s3://${Bucket}/${AWS::StackName}/${StackType}-API.yaml
This will resolve like this:
s3://example-bucket/name-of-stack/typeA-API.yaml
or
s3://example-bucket/name-of-stack/typeB-API.yaml
So now I just need to have these files named in that way on the S3, this is similar to what mapping should do, but for now, is not supported, this will achieve what I want, is not perfect but did the trick, still remain the confusion because I think the documentation is inaccurate

AWS CDK generated resource identifiers are horrible and not readable. Any way to fix this?

Anyone, that has used AWS CDK suffers from horrible resource identifiers.
Examples of Stacks/Nested Stacks names:
Or examples of resource names:
These identifiers are horrible to read. Is there any work-around to override these identifiers?
I have tried to set ids / names / identifiers / alies of the resources. However it seems that cdk or cloudformation itself is generating these strings.
Thank you for suggestions!
All of resources(or at least for most that I know) could be named manually.
For AWS::EC2::SecurityGroup that would be Properties -> GroupName
AWS::CloudWatch::Alarm - Properties -> AlarmName
AWS::Lambda::Function - Properties -> FunctionName
etc.
But for some of them that would lead to consequences - you won't be able to update some of them, because they might need recreation (and the name is already occupied). So in general it's not a good practice.
And obviously you won't be able to create a full env duplicate not changing some parameter for the generated name like this:
FunctionName: !Sub '${InstanceName}-your-resourse-constant-name-${Environment}'
If you don't specify the naming it would create a name like this:
${stackName}-${resourceNameInCF}-${someHashCode}, but in your case it seems you have nested stacks and it becomes pretty unreadable, especially with long names because of the names chaining.
Yeah, this is a good question. There's two types of IDs here:
Logical ID
Physical ID
Typically the Physical ID can be specified as a property on the resources. For instance, if you are using the CDK, you can set the functionName property when creating your Lambda (as below).
The Logical ID is also added when creating the resource and as you mentioned, however, the Logical ID is derived from a combination of what you specify and where it is within your stack. So, for example, if you have a stack that uses constructs, then this ID will be prefixed with the construct's Logical ID as well... and it's definitely not very readable.
I'd be very careful changing these IDs, especially if you have already deployed the stack, but if you really want to override them then you could do something like this in the CDK (TypeScript):
import {
CfnResource,
} from "#aws-cdk/core";
import {
Function,
Runtime,
Code,
} from "#aws-cdk/aws-lambda";
const consumerLambda = new Function(this, 'LogicalIdOnResource', {
runtime: Runtime.NODEJS_12_X,
handler: 'index.handler',
code: Code.fromAsset(path.join(__dirname, 'lambda-handler')),
functionName: 'ds-di-kafka-consumer-lambda' // PhysicalIdOnResource
});
// Override Logical ID
(consumerLambda.node.defaultChild as CfnResource).overrideLogicalId(
'Consumer'
);
Which looks like this on CloudFormation:

Is there a global properties for cloudformation Resources section?

I have a cloudformation template for my lambda:
Resources:
Resource1:
Type: AWS::Res
Properties:
StreamArn:
"Fn::Sub": "${var1}-${var2}"
Resource2:
Type: AWS::Res
Properties:
StreamArn:
"Fn::Sub": "${var1}-${var2}"
Is it possible to move these properties somewhere to Properties field of Resources section or any other place to avoid duplication?
Resources:
Properties:
StreamArn:
"Fn::Sub": "${var1}-${var2}"
I've tried to do it, but it doesn't work.
You can use a Parameters entry with a default value to create the equivalent to a Constant Variable, but it can't accept any values from the Resources section (since they haven't been created at that point).
Otherwise, no -- you'll need to duplicate the values. (As at the time of writing this answer.)
If you're using AWS::Serverless::Function, you can use Globals section to have common properties in a stack in one place. So, you can put the resources you mentioned in a stack for them and define a Globals section that has StreamArn
See docs
If you are using SAM and the right resources then you can use the globals sections for this:
AWS::Serverless::Function
AWS::Serverless::Api, and
AWS::Serverless::SimpleTable
The Globals section is unique to AWS SAM. It defines properties that
are common to all your serverless functions and APIs. All the
AWS::Serverless::Function
AWS::Serverless::Api, and
AWS::Serverless::SimpleTable
resources inherit the properties that are
defined in the Globals section. For more information about the Globals
section, see Globals Section of the Template in the AWS Serverless
Application Model Developer Guide.
Documentation
The most convenient way that I found so far is to use mapping like:
Mappings:
ParametersMap:
Var1:
Value: "A"
Var2:
Value: "B"
and then put line !FindInMap: [ "ParametersMap", "Var1", "Value" ] in all the places were you need Var1 param

CloudFormation: conditional parameters

Building a CloudFormation stack template, I have a setup constellation where upon instantiation I want to reference either the name of another CloudFormation stack or a non-CloudFormation-managed database as a parameter.
Is there a way to represent this constellation in my template? I.e. "Parameter DatabaseHost is mandatory if Parameter DatabaseStack is blank"?
Maybe it wasn't possible at the time of the question, but now, you can include conditions on a CloudFormation template. See docs.
In this example, I use one value or another depending on the environment:
InfrastructurePipelineStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Sub "https://<masked>.yml"
Parameters:
ProjectName: !Ref ProjectName
...
LambdaNotifications: !If [isDev, !GetAtt NotificationsStack.Outputs.LambdaNotifications, !Ref LambdaNotifications]
If the environment is Development ("isDev" condition), I use the output of other CloudFormation Stack as the value. If not, I use a provided fixed value (non-CloudFormation value).
In this case "isDev" is acting as "parameter DatabaseStack is blank" in the OP question.
I'm not aware of a native option in CloudFormation to make one template parameter conditional on a second template parameter.
Possible workarounds might be:
make both optional, and tell user to supply one of them
use two templates, one for each of the two use cases
programmatically generate your template after asking the user for parameters