Cloudformation AWS::ElasticLoadBalancingV2::Listener arbitrary list of certificates - amazon-web-services

I'm trying to write a CloudFormation template that will support multiple environments, and each env can have 2 or 3 certificates that should be attached to the load balancer listener.
However, since Cloudformation doesnt accept a simple list of certificate arns (doc), I'm struggling to figure out how to pass in a list of objects using parameters. It seems that:
Each parameter MUST have a type
"List" is not an acceptable type, it must be a list "of something"
Cloudformation does not have a "foreach" to create a "cert object" from each certificate arn on-the-fly.
I came across this example template (link), but here the problem is solved by using two explicit certificates - I need to be able to dynamically input an arbitrary list of certificates
For ref, I need to be able to inject something like this (pseudocode):
Certificates:
- Certificate: Arn1
- Certificate: Arn2
- Certificate: Arn_n
Where Arn1, Arn2, Arn_n comes from a "list-type" Parameter.
So I guess the question is: Is there any "foreach" or anything else in Cloudformation that could help me generate one "object" per item in a parameter list, or any way to pass in "rich" parameter objects?

CloudFormation allows you to use a CommaDelimitedList as a parameter type. You can then supply this in the form of a comma-separated list:
arn1,arn2,arn3
The list is arbitrary. You can then supply this into your listener's Certificates property directly:
"Certificates" : { "Ref" : "MyListParam" }

Related

AWS Proton parameters - clarify how the schema.yaml parameters are consumed in CF template

After going through the docs and examples I haven't clarified where exactly the parameters from the schema.yaml file are used.
Using the AWS code example here: https://github.com/aws-samples/aws-proton-sample-templates/blob/main/lambda-crud-svc/service/schema/schema.yaml
Pertinent section of the schema.yaml file:
schema:
format:
openapi: "3.0.0"
service_input_type: "CrudServiceInput"
pipeline_input_type: "PipelineInputs"
types:
CrudServiceInput:
type: object
description: "Input properties for a Lambda backed CRUD API. When given a resource name, this input will be used to generate Create, Read, Update and Delete API methods and lambdas."
properties:
resource_name:
type: string
description: "The resource to generate a CRUD API for"
minLength: 1
maxLength: 50
default: "greeting"
resource_handler:
type: string
description: "The handler path to find the CRUD methods for this API"
minLength: 1
maxLength: 50
default: "index"
lambda_memory:
type: number
description: "The size of your Lambda functions in MB"
default: 512
minimum: 1
maximum: 3008
...
I would expect that in the cloudformation.yaml file I would be able to reference {{service_input_type.resource_name}}, but it is referred to as {{service.resource_name}}.
Assume that Proton somehow has service namespace mapped to the values in service_input_type.
HOWEVER
When you use that logic for "lambda_memory" parameter in the same service_input_type object, it doesn't work, because in the template file it refers to this as service_instance.lambda_memory
Can anyone clarify the following:
How are the schema.yaml parameters consumed in the
cloudformation.yaml template?
Further... how do the "xx-spec.yaml"
files play into the mix. I assume they are merged into the service
template when creating the instance, but the parameter naming
convention is also different than the template parameters above.
You should always use service_instance as the namespace, with the exception of
if you are parametrizing a pipeline template, where you can use service;
if you are referring to the name, where you can use the service_name and service_instance_name variables (no namespace).
A Proton service is ultimately a collection of service instances with a pipeline. When Proton provisions infrastructure, it will create a CloudFormation stack for every instance, so the parameters are always applied at the instance level. The pipeline is an exception because it doesn't belong to a single environment, and so in certain cases, you might need to refer to the service as a whole.
As per the xx-spec.yaml files - Those are actually the reflections of the parameters that the developer might provide. If you use the Proton UX to create a new service, Proton will output a file like that and store it to reflect the parameters for that particular service.

How to reference CloudFormationProvisionedProduct Outputs

I need to provision AWS resources via AWS Service Catalog. I have a 'product' which i can provision and then i need to reference some property available in Outputs of provisioned product resource.
I wanted to reference the value within Outputs of AWS::ServiceCatalog::CloudFormationProvisionedProduct and hoped that it works same way as Outputs of a stack.
I tried !GetAtt http:// ProvisionedProductName, Outputs.InnerProperty - did not work, i get Requested attribute Outputs.InnerProperty must be a readonly property in schema for AWS::ServiceCatalog::CloudFormationProvisionedProduct
The Outputs attribute exists, however i am not sure how to explode this list and get an inner property.
I tried to use !Select to reference a list item, however it does not seem to work either:
!Select [1, !GetAtt https://forums.aws.amazon.com/
Template error:
Fn::Select requires a
list argument with two
elements: an integer
index and a list
Help appreciated
They recently launched this exact feature, so "!GetAtt YOURPROVISIONEDPRODUCTNAME.Outputs.YOUROUTPUTNAME" should work.
https://aws.amazon.com/about-aws/whats-new/2020/07/provisioned-product-outputs-now-available-aws-service-catalog/
This blog was updated too: https://aws.amazon.com/blogs/mt/how-to-launch-secure-and-governed-aws-resources-with-aws-cloudformation-and-aws-service-catalog/, search for ".Outputs."

How to create metrics/alarms for AWS Logs SubscriptionFilter using CDK?

Context
I have created a AWS Logs SubscriptionFilter using CDK. I am now trying to create a metric/alarm for some of the metrics for this resource.
Problem
All the metrics I am interested in (see ForwardedLogEvents, DeliveryErrors, DeliveryThrottling in the Monitoring AWS Logs with CloudWatch Metrics docs) requires these dimensions to be specified:
LogGroupName
DestinationType
FilterName
The first two are easy to specify since the LogGroupName is also required while creating the construct and DestinationType in my case is just Lambda. However, I see no way to get FilterName using CDK.
Using CloudWatch, I see that the FilterName is like MyStackName-MyLogicalID29669D87-GCMA0Q4KKALH. So I can't directly specify it using a Fn.ref (since I don't know the logical id). Using CloudFormation, I could have directly done Ref: LogicalId.
I also don't see any properties on the SubscriptionFilter object that will return this (unlike most other CDK constructs this one seems pretty bare and returns absolutely no information about the resource).
There are also no metric* methods on SubscriptionFilter object (unlike other standard constructs like Lambda functions, S3 buckets etc.), so I have to manually specify the Metric object. See for example: CDK metric objects docs.
The CDK construct (and the underlying CloudFormation resource: AWS::Logs::SubscriptionFilter) does not let me specify the FilterName - so I can't use a variable to specify it also and the name is dynamically generated.
Example code that is very close to what I need:
const metric = new Metric({
namespace: 'AWS/Logs',
metricName: 'ForwardedLogEvents',
dimensions: {
DestinationType: 'Lambda',
// I know this value since I specified it while creating the SubscriptionFilter
LogGroupName: 'MyLogGroupName',
FilterName: Fn.ref('logical-id-wont-work-since-it-is-dynamic-in-CDK')
}
})
Question
How can I figure out how to acquire the FilterName property to construct the Metric object?
Or otherwise, is there another way to go about this?
I was able to work around this by using Stack#getLogicalId method.
Example code
In Kotlin, as an extension function for any Construct):
fun Construct.getLogicalId() = Stack.of(this).getLogicalId(this.node.defaultChild as CfnElement)
... and then use it with any Construct:
val metric = Metric.Builder.create()
.namespace("AWS/Logs")
.metricName("ForwardedLogEvents")
.dimensions(mapOf(
"DestinationType" to "Lambda",
"LogGroupName" to myLogGroup.logGroupName,
"FilterName" to mySubscriptionFilter.getLogicalId()
))
.statistic("sum")
.build()

Unresolved Parameter Store SecureString dynamic reference in CloudFormation template

According to AWS, when deploying infrastructure that requires secrets, i.e. passwords or similar, with CloudFormation, one popular solution consists in using the Parameter Store's SecureStrings from SSM.
However, despite existing CFN documentation describing step-by-step how to use the Dynamic References within the CFN templates, I can not manage to make use of the actual value of the SecureStrings.
Assuming the following JSON representation of the an existing SecureString stored in the SSM Parameter Store:
{
"MyRedshiftMasterUserPassword": {
"value": "Abcd2019",
"type": "SecureString"
}
}
and a YAML CFN template that uses it as stated in the documentation:
Resources
Redshift:
Type: 'AWS::Redshift::Cluster'
Properties:
NodeType: dc2.large
NumberOfNodes: !Ref RedshiftNodes
ClusterType: multi-node
AutomatedSnapshotRetentionPeriod: !Ref AutomatedSnapshotRetentionPeriod
DBName: datawarehouse_v1
MasterUsername: !Ref RedshiftMasterUsername
MasterUserPassword: '{{resolve:ssm-secure:MyRedshiftMasterUserPassword:1}}'
The above solution does not to work so either I am defining the template incorrectly or the support for this feature is not properly implemented which seems odd to me considering that it comes from AWS.
Particularly, I came across the following errors that all end up as UPDATE_FAILED stack:
Whenever the referenced Parameter Name to be resolved is long enough CloudFormation complains:
The parameter MasterUserPassword is not a valid password because it is longer than 64 characters. (Service: AmazonRedshift; Status Code: 400; Error Code: InvalidParameterValue; Request ID: 7be9bd43-2927-11e9-aa88-29bbdcae859e)
Additionally, even though specifically mentioned that slashes can be used in the template references, e.g. /infrastructure/datawarehouse/redshift/MyRedshiftMasterUserPassword following error is issued:
The parameter MasterUserPassword is not a valid password. Only printable ASCII characters except for '/', '#', '"', ' ', '\', ''' may be used. (Service: AmazonRedshift; Status Code: 400; Error Code: InvalidParameterValue)
thus, as result referenced SecureString do not seem to be compatible with the SSM ParameterStore hierarchy (parameters with slashes).
Moreover, removing any of the previously reported invalid character from the parameter name, then it complains of the following:
The parameter MasterUserPassword must contain at least 1 upper case letter. (Service: AmazonRedshift; Status Code: 400; Error Code: InvalidParameterValue; Request ID: 90a263bd-2929-11e9-80c0-ffcecf297c44)
In the end, although using a basic short non-slashed Parameter name in a template allows the stack to finish the Update operation dynamic reference still does not occur as the actual value used turns out to be supplied Parameter Name rather than the value referenced by this, e.g MyRedshiftMasterUserPassword instead of Abcd2019.
I am aware that AWS Secrets Manager could also be used but it is not free.
Opened support case with AWS requesting guidance for this particular strange behaviour of CloudFormation.
According to the support team, effectively this is indeed a known bug for the CloudFormation service without estimated time to fix though.
The resolution of the a SSM Parameter Store SecureString parameter when used as a Dynamic Reference in the particular case of the RedshiftMasterUserPassword property despite referenced in the documentation does not get properly resolved and parameter name is used instead.
Alternatively, they offer 2 workarounds whilst the issue gets fixed:
Get the 'MasterUserPassword' for Redshift from input parameter with property NoEcho set to true. NoEcho property allows you to mask the password value and you don't need to store the password in a template file. However, each time you update the stack you need to enter the password as input parameter. For your reference, below code snippet will be useful.
Second option, which is more versatile:
Define a Lambda backed Custom Resource in your template file, which queries the SSM service and returns the password to CloudFormation. In this scenario, You need to write a Custom Code for lambda function which uses AWS GetParameter API call to retrieve the value of SSM Secure String Parameter and returns the decrypted value to CloudFormation.
Other supported properties for dynamic reference seem to work fine.

How to delete aws-lambdas based on their tag

I have few lambdas created by automation using Ansible.
- lambda:
name: 'NAME'
state: present
zip_file: 'index.js.zip'
tags:
createdBy: 'ansible'
And few more lambdas which are created manually.
I would like to delete all lambdas which are created by "Ansible", so I added "tag" attribute to all the automated lambdas.
I know, we can delete lambda if we have its name, but I would like to get all lambdas and filter the lambda which has tags['createdBy']='ansible.
lambda_facts are a way to get all lambda configuration, but it doesn't give me tag details.
How do I delete lambdas by filtering tags ?
I suggest to get all your functions with help of lambda.listFunctions and run lambda.listTags for each Lambda and build your own array with all functions which includes ansible as a tag.
In the end iterate over this array and call lambda.deleteFunction for each entry.
(I dont know which language you prefer. All my examples are using the JavaScript SDK)