Cloudformation to automatically pick up the default instance profile - amazon-web-services

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"] },

Related

CloudFormation template - Using existing IAM role in for Lambda functions

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.

Optional parameters when using AWS CLI to launch CloudFormation template

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" : ""
}
}

Does CloudFormation only support "Type": "String" when declaring parameters?

When you declare parameters in CloudFormation, you have to set a "Type". In my experiments, "String" seems to be the only supported value. If that is true, what is the purpose of this attribute?
In the AWS Documentation on parameters, it is not mentioned which values are supported for the "Type". It contains an example, however, that indicates that there is no integer type. The "WebServerPort" is also declared as "String", although ports are always integers:
"Parameters" : {
"InstanceType" : {
"Type" : "String",
"Default" : "m1.small",
"Description" : "EC2 instance type, e.g. m1.small, m1.large, etc."
},
"WebServerPort" : {
"Type" : "String",
"Default" : "80",
"Description" : "TCP/IP port of the web server"
},
"KeyName" : {
"Type" : "String",
"Description" : "Name of an existing EC2 KeyPair to enable SSH access to the web server"
}
}
Besides String, there is Number, List<Number>, CommaDelimitedList, and a variety of AWS-Specific Parameter Types:
AWS::EC2::AvailabilityZone::Name
AWS::EC2::Image::Id
AWS::EC2::Instance::Id
AWS::EC2::KeyPair::KeyName
AWS::EC2::SecurityGroup::GroupName
AWS::EC2::SecurityGroup::Id
AWS::EC2::Subnet::Id
AWS::EC2::Volume::Id
AWS::EC2::VPC::Id
AWS::Route53::HostedZone::Id
List<AWS::EC2::AvailabilityZone::Name>
List<AWS::EC2::Image::Id>
List<AWS::EC2::Instance::Id>
List<AWS::EC2::SecurityGroup::GroupName>
List<AWS::EC2::SecurityGroup::Id>
List<AWS::EC2::Subnet::Id>
List<AWS::EC2::Volume::Id>
List<AWS::EC2::VPC::Id>
List<AWS::Route53::HostedZone::Id>
Refer to the Properties section of the Parameters CloudFormation documentation for more detail.
Note that the documentation linked to in the question was specifically regarding the Parameters Property within the AWS::CloudFormation::Stack Resource, which is why the documentation was limited there. The Template Anatomy section of the guide has more complete information on template structure.

Parameter validation failed: parameter value for parameter name <some parameter> does not exist

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.

How can we reuse configured AMI in the same template with the same userdata configs

I have created instance using cloudformation template, configured it with userdata configuration and powershell dsc. I have created AMI for this instance so that next time it speeds up my stack creation.
Now how can i use the this AMI in that same template so it bypass all the configurations & installation done on instance and directly sends success signal to waithandler.
I am trying this in my template but it is failing.
Thanks in Advance,
Lokesh Jangir
It sounds like you need a check in your user data to see if everything is already configured, and if it is, then you just stop and send the notification instead of setting it back up again.
Ultimately, it sounds like it'd be easier to have two templates - one to create the AMI, and one to re-use it in other settings. The second template could take the AMI ID as a parameter so that it is more flexible and can be used with different AMIs as you create them.
1. To use your AMI id in the cloudformation template, start with adding a parameter, so that you can easily change it:
`
"Parameters": {
...
"amiId": {
"Type": "String",
"Default": "ami-073bb070",
"AllowedPattern": "[a-zA-Z0-9\\-]*",
"Description": "Only [a-zA-Z0-9\\-]* allowed."
},
...
}
2. Use that param in a LaunchConfig:
`
"aLaunchConfig": {
"Type": "AWS::AutoScaling::LaunchConfiguration",
"Properties": {
"ImageId": { "Ref" : "amiId" },
...
3. or use it directly in an EC2 instance:
`
"someEC2": {
"Type": "AWS::EC2::Instance",
"Properties": {
"ImageId": { "Ref" : "amiId" },