I'm trying to create a CloudFormation template that'll deploy a Lambda function, And I need the security options to be optional parameters.
I was able to partially accomplish this using the question here:
How to make a whole object in CloudFormation templates optional?
Interestingly, that method worked great to make the VpcConfig property optional in the AWS GUI Console, but it did NOT work to make it optional for the CLI. And unfortunately, I need it to work in the CLI, since I'll be using CodeBuild to call and deploy this template's resources.
Here are the relevant parameters:
"SecurityGroupIds" : {
"Type" : "CommaDelimitedList",
"Description" : "A list of one or more security groups IDs in the VPC that includes the resources to which your Lambda function requires access."
},
"SubnetIds" : {
"Type" : "CommaDelimitedList",
"Description" : "A list of one or more subnet IDs in the VPC that includes the resources to which your Lambda function requires access."
}
And conditions:
"HasVPC": {"Fn::And": [{"Fn::Not": [{"Fn::Equals": [{"Fn::Join": ["", {"Ref": "SubnetIds"}]}, ""]}]}, {"Fn::Not": [{"Fn::Equals": [{"Fn::Join": ["", {"Ref": "SecurityGroupIds"}]}, ""]}]}]}
And here's where that condition is used in the Lambda resource being defined in the Resources section of the template:
"VpcConfig": {
"Fn::If": [
"HasVPC",
{
"SecurityGroupIds" : {"Ref": "SecurityGroupIds"},
"SubnetIds" : {"Ref": "SubnetIds"}
},
{ "Ref":"AWS::NoValue" }
]
},
When I issue the command to deploy this stack in the CLI, I get the following error:
An error occurred (ValidationError) when calling the CreateChangeSet
operation: Parameters: [SecurityGroupIds, SubnetIds] must have values
Here's the AWS CLI command I'm issuing, from the same directory in which the template is located. Note: the ARN values have all been heavily modified to not be real values from my account, but I kept them in the right format so you can see the real format of the command:
aws cloudformation deploy --template-file lambda-template.json --stack-name "CLI-lambda-stack" --parameter-overrides S3BucketName="myBucket" S3FileLocation="lambda_function.zip" S3ObjectVersion="ZuB0iueEghOyh5q00.DiykLNudujdsc5" DeadLetterArn="arn:aws:sns:us-west-2:577898337216:CloudFormationTests" EnvironmentVariable="testing" KmsKeyArn="arn:aws:kms:us-west-2:504398934246:key/b24e7b72-a94d-6a3e-b848-165115c86212" HandlerFunctionName="lambda_function.lambda_handler" MemorySize="128" Role="arn:aws:iam::102893937243:role/serverless-test-default-us-east-1-lambdaRole" FuncName="myCLILambda"
You are not providing SecurityGroupIds neither SubnetIds default values and your are not providing them on your --parameter-overrides. Therefore, CloudFormation doesn't know how to process them if no values are provided.
Adding the Default statement should do the trick:
{
"Parameters" : {
"SecurityGroupIds" : {
"Type" : "CommaDelimitedList",
"Description" : "A list of one or more security groups IDs in the VPC that includes the resources to which your Lambda function requires access.",
"Default" : ""
},
"SubnetIds" : {
"Type" : "CommaDelimitedList",
"Description" : "A list of one or more subnet IDs in the VPC that includes the resources to which your Lambda function requires access.",
"Default" : ""
}
}
Related
I'm trying to use an existing role (present in the AWS account) in a cloudformation template to setup a lambda function, i plan to be use this across multiple AWS accounts.
In the CF template, I'm using Parameters to set the name of the Role and then using Ref in the Role property for the Lambda function. This is what my template looks like,
"Parameters" : {
"ExistingRoleName" : {
"Type" : "String",
"Default" : "MyCustomRole"
}
"Resources" : {
"CustomLambdaFunction" : {
"Type" : "AWS::Lambda::Function",
"Properties" : {
"MemorySize" : "128",
"Role" : { "Ref" : "ExistingRoleName" },
}
},
...
However, the CF template fails with the following error :
Properties validation failed for resource CustomLambdaFunction with message: #/Role: failed validation constraint for keyword [pattern]
Is this because Lambda resource in Cloudformation needs the role arn instead of RoleName as i seen in this docaws-resource-lambda-function
Based on which i updated the CF like so,
"Resources" : {
"CustomLambdaFunction" : {
"Type" : "AWS::Lambda::Function",
"Properties" : {
"MemorySize" : "128",
"Role" : "arn:aws:iam::AccountID:role/MyCustomRole",
}
},
However, i still see the same error.
Properties validation failed for resource CustomLambdaFunction with message: #/Role: failed validation constraint for keyword [pattern]
I was wondering if i'm missing something here ?
The Ref of an IAM Role “returns the resource name”, not its ARN. But you can use GetAtt on the Arn attribute of the role instead.
In JSON:
{"Fn::GetAtt": ["MyRole", "Arn"]}
In YAML:
!GetAtt MyRole.Arn
Format to reference the iam role arn
"Role" : { "Fn::Sub" : "arn:aws:iam::${AWS::AccountId}:role/MyCustomRole" }
In yaml if you are pointing to an already existing role the syntax is:
function:
...
role: !Sub arn:aws:iam::${AWS::AccountId}:role/MyRoleName
Somehow I have forgotten the !Sub in the beginning
This is what worked for me,
"Role": { "Fn::Join" : [ "", [ "arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":role/MyCustomRole" ] ] }
I was getting the same problem with below syntax -
"Resources" : {
"CustomLambdaFunction" : {
"Type" : "AWS::Lambda::Function",
"Properties" : {
"Role" : "arn:aws:iam::<account-id>:role/MyCustomRole",
}
},
I solved it like this -
The issue was that when inserting my AWS account ID in place of "account-id", I was keeping it in the same format as is given on the AWS console i.e. xxxx-xxxx-xxxx. However, the "account-id" space expects "\d{12}" format, i.e. 12 digits only. Removing the '-' in between digits solved the problem for me.
I have a user who can create roles iff there is an attached permission boundary. The user can execute this function via the AWS console and via API calls from the API. However, there does not seem to be a way to automate the process in CloudFormation. Is it possible to create a role in a CFT and attach a permissions boundary to it?
Permissions Boundary is now supported by the CloudFormation's schema https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html.
{
"Type" : "AWS::IAM::Role",
"Properties" : {
"AssumeRolePolicyDocument" : Json,
"ManagedPolicyArns" : [ String, ... ],
"MaxSessionDuration" : Integer,
"Path" : String,
"PermissionsBoundary" : String,
"Policies" : [ Policy, ... ],
"RoleName" : String
}
}
YAML
Type: AWS::IAM::Role
Properties :
AssumeRolePolicyDocument : Json
ManagedPolicyArns :
- String
MaxSessionDuration : Integer
Path : String
PermissionsBoundary : String
Policies :
- Policy
RoleName : String
As an aside, the support for it was available in Terraform before it was in CFT.
Any suggestions why this AWS CloudFormation keeps rolling back?
{
"Description" : "Single Instance",
"Resources" : {
"EC2Instance" : {
"Type" : "AWS::EC2::Instance",
"Properties" : {
"ImageId" : "ami-b73b63a0",
"InstanceType" : "t2.micro",
"KeyName" : "aws-key-here",
"Tags" : [
{
"Key" : "Name",
"Value" : "test"
}
],
"SubnetId" : {
"Fn::Select" : [ "0", { "Ref" : "Subnets" } ]
}
}
}
},
"Parameters": {
"Subnets": {
"Type": "List<AWS::EC2::Subnet::Id>",
"Description": "The list of SubnetIds, for at least two Availability Zones in the region in your Virtual Private Cloud (VPC)"
}
}
}
The specific error seems to be about the Subnets Ref:
Parameter validation failed: parameter value for parameter name Subnets does not exist. Rollback requested by user.
I have already created 1 valid Subnet in my AWS Management Console and tested that it works when spinning up an EC2 Instance manually.
Or is there a way of debugging this / getting more detailed output?
The Subnets Parameter in your template has a type List<AWS::EC2::Subnet::Id>, which requires a reference to a list of valid Subnet IDs. The error you are seeing means that you passed at least one invalid Subnet ID to the Subnet parameter.
If you're deploying your stack from the AWS CLI using aws cloudformation create-stack, you need to pass a valid parameter value using the --parameters ParameterKey=Subnets,ParameterValue=subnet-12345678 option.
If you're deploying from the Management Console, you need to specify stack parameters using the provided dialog, and select a Subnet ID from the drop-down list.
I faced the same problem. In my case I created the Key with name "mykey" in Mumbai Region. But when I actually started creating a Cloud Formation Infrastructure I changed the region to US-East. Now in my Cloud Formation Infrastructure I provided the name of Key as "mykey". Now the problem is the key "mykey" was created in Mumbai Region.
Hence I create the Key once again in "US-East" region and my problem vanished.
Regards
Hitesh
In my case it was a problem on credentials key and access key. My default values were another accounts one and I was trying to create a stack on a wrong account.
I use a CloudFormation template to deploy an instance to the environment. I want the template to pick up the default EC2 instance profile for the instance which is something like "arn:aws:iam::12345678910:role/EC2InstanceProfile-InstanceRole-14F2A0ATJNUO1"
I would like to use the same template for every AWS accounts I have. However, the problem is that the instance profile name is different in every account. A randomly generated suffix is attached to the name (in this example 14F2A0ATJNUO1). How can I workaround this problem to make the template reusable in every account. Please provide the code if possible.
"EC2InstanceProfile" : {
"Description" : "The default instance profile",
"Type": "String",
"ConstraintDescription" : "must be the name of an existing defualt EC2 instance profile."
},
"IamInstanceProfile": { "Ref": "EC2InstanceProfile" }
Get your instance profile from the role name using the below cloudformation.
"InstanceProfile" : {
"Type" : "AWS::IAM::InstanceProfile",
"Properties" : {
"Path" : "/",
"Roles" : ["your-role-name"]
}
}
"IamInstanceProfile": {"Fn::GetAtt" : ["InstanceProfile", "Arn"] },
We wanted to use company specific Tags to the resources that we create in AWS for billing purposes. I am using a cloud formation template to spin up our Elasticbeanstalk instance and other project dependent resources. When I use the CloudFormation console to create a stack it asks me for Tags in the page after parameters. I have to manually input the Tags for that stack. However is there a way to specify those Tags (Tags for the stack) with in the cloud formation template itself? That way the Tag gets propagated to the other resources? I know that the cloud formation automatically tags the resources with the stack name. But we need company specific tags to bill separate departments.
In the template anatomy, you can't set stack-level tags directly. However you can create a wrapper template, having a single resource of AWS::CloudFormation::Stack.
You can define stack-level tags on that resource:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "WrapperTemplate",
"Resources": {
"WrappedStackWithStackLevelTags": {
"Type" : "AWS::CloudFormation::Stack",
"Properties" : {
"Tags" : [ { "Key" : "Stage", "Value" : "QA" } ],
"TemplateURL" : "your-original-template-s3-url"
}
}
}
}
When launching AWS CloudFormation, the tags being requested will be applied to the CloudFormation Stack itself and (where possible) will also be propagated to the resources launched by the Stack.
These tags can be passed to the CreateStack API call, or from the CLI:
See: create-stack CLI documentation
These tags are applied to the whole Stack and aren't included in the CloudFormation template.
However, CloudFormation templates can include tags for specific resources that are being created. For example, when launching Amazon EC2 instances, tags can be included in the template:
"MyInstance" : {
"Type" : "AWS::EC2::Instance",
"Properties" : {
"SecurityGroups" : [{ "Ref" : "MySecurityGroup" }],
"AvailabilityZone" : "us-east-1a",
"ImageId" : "ami-20b65349",
"Volumes" : [{
"VolumeId" : { "Ref" : "MyEBS" },
"Device" : "/dev/sdk"
}],
"Tags" : [{
"Key" : "Stage",
"Value" : "QA"
}]
}
}
Contrary to what #lalyos says, you don't need to use nested stacks for this, just provide the tags that should apply to all resources as stack level tags.
These stack-level tags can be specified whether running the stack on the console or via CLI.
CLI example:
aws cloudformation create-stack --stack-name my-stack-name \
--template-body file://path-to-template-file.yaml \
--parameters ParameterKey=param1key,ParameterValue=param1value \
--tags Key=tag1key,Value=tag1value \
Key=tag2key,Value=tag2value \
Key=tag3key,Value=tag3value
... and generally add as many tags as you need, using the same format and allowing spaces between tag key-value pairs
See:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-console-add-tags.html
You can build and deploy a CloudFormation template using aws-sam-cli. This command reads a samconfig.toml file where you can declare tags for all the resources of the stack (including CloudFormation stack itself)
Your samconfig.toml should look like:
[default.deploy.parameters]
stack_name = "your-application"
s3_bucket = "your-s3-for-cloudformation-stuff"
s3_prefix = "your-folder-name"
...
tags = "Stage=\"QA\""
and then run:
sam build --template <your-cloudformation-template.yml> && sam deploy
You don't need any wrapper..
You can add tags to the stack on when you create/update it:
In Console:
You can also use the aws cli:
aws cloudformation create-stack help
--tags (list)
Key-value pairs to associate with this stack. CloudFormation also
propagates these tags to supported resources in the stack. You can
specify a maximum number of 50 tags.
If you don't specify this parameter, CloudFormation doesn't modify
the stack's tags. If you specify an empty value, CloudFormation re-
moves all associated tags.
(structure)
The Tag type enables you to specify a key-value pair that can be
used to store information about an CloudFormation stack.
Key -> (string)
Required . A string used to identify this tag. You can spec-
ify a maximum of 128 characters for a tag key. Tags owned by
Amazon Web Services (Amazon Web Services) have the reserved
prefix: aws: .
Value -> (string)
Required . A string containing the value for this tag. You
can specify a maximum of 256 characters for a tag value.
Shorthand Syntax:
Key=string,Value=string ...
JSON Syntax:
[
{
"Key": "string",
"Value": "string"
}
...
]