Existing dynamic AWS resources in cloudformation template - amazon-web-services

I have already created AWS network resources(VPC,subnets,IGW etc) through AWS console.
Now, I am trying to create a cloudformation stack consisting of those existing & new resources.
To implement existing resources in the stack, I am using Parameters
Parameters:
VpcId:
Description: VPC Id
Type: AWS::EC2::VPC::Id
PublicSubnetId1:
Description: Public Subnet 1
Type: AWS::EC2::Subnet::Id
PublicSubnetId2:
Description: Public Subnet 2
Type: AWS::EC2::Subnet::Id
InternetGateway:
Type: AWS::EC2::InternetGateway::Id
Question 1: Is this the right approach ? (As we have diff envs so to handle dynamic AWS resources, I am doing this)
Question 2: I am able to set parameter with VPC,subnets but not with Internet gateway. How to put internetgateway as a parameter ?
Thanks

Question 1: Is this the right approach ? (As we have diff envs so to handle dynamic AWS resources, I am doing this)
Its one way of doing this. If you want to be able to select different subnet or vpc at template creation then its fine. However, if the VPC and subnets are fixed and do not change, probably better would be to export them from their own stacks, end them import them in other stacks. This way other stacks can easily refer to them, and you have simplified deployment your stacks as you don't need to provide all these parameters for each new stack.
Question 2: I am able to set parameter with VPC,subnets but not with Internet gateway. How to put internetgateway as a parameter ?
Unfortunatly, there is no such parameter like AWS::EC2::InternetGateway::Id. In this case you would have to use String:
InternetGateway:
Type: String

Related

Creating subnet per availability zone using cloudformation

I need to create subnet per availability zone in particular region using cloudformation.
For example if the region is Mumbai, it is with three availability zones and the CF template should create a public and a private subnet in each availability zone: 1a,1b and 1c. Is it really possible? I have done the same using terraform but have no idea how can I achieve this in CF.
It would be great if someone could help on this.
Thanks in advance.
Sadly, there are no loops in plain CloudFormation. Thus you can't create any constructs that would loop over AZs, get their IDs and create a pair of private-public subnets in each AZ.
If you really want to keep everything in CloudFormation than you would have to look at custom resources or marcros.
Both of them would require you to write your own lambda function that would use AWS API to get the number of AZs, their names and perform iteration to create the subnets.
If you already are using terraform successful, maybe its worth considering to keep using it, as it has loops useful in your use-case.
You might be able to write it in cloudformation. For example with Fn::If and then using Fn::GetAZs and creating the resources only if enough azs a parameter you can hardcode.
PrivateSubnet6:
Condition: Has6AZs
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: MyVPC
CidrBlock: 10.0.20.0/22
AvailabilityZone:
Fn::Select:
- 5
- Fn::GetAZs: !Ref 'AWS::Region'
Tags:
- Key: "Name"
Value: "PrivateSubnet6"
Has6AZs:
Fn::Equals: [!Ref AWS::Region, "us-east-1"]

AWS CloudFormation Outputs: Exporting AZ's

Creating a layered stack set in cloudformation. Network Stack and App Stack
Just need the syntax to Output and Export two Availability Zones that are captured when a cfn user chooses them in the network template parameters dialogue.
eg, a user chooses two AZ's in a region via the usual mechanism.
AZoneNames:
Type: 'List<AWS::EC2::AvailabilityZone::Name>'
Description: Availability Zones (choose two zones)
That captures the az's, and i assume, cfn indexes them [0,1] to an array in the background. That part works.
So I need to output the two az's and export them for the app stack but not sure how. I've attempted with the below snippet but it doesnt work
StackAvailabilityZone1:
Description: The first az that was chosen at network stack creation
Value: !Ref AvailabilityZone 0
Export:
Name: !Sub 'AZ1'
I'm sure its probably staring me in the face. Thanks so much for any ideas.
You can try the following, using Select:
StackAvailabilityZone1:
Description: The first az that was chosen at network stack creation
Value: !Select [0, !Ref AZoneNames]
Export:
Name: AZ1

Is it possible to execute commands and then update security groups in a CloudFormation template?

I would like to perform the following operations in order with CloudFormation.
Start up an EC2 instance.
Give it privileges to access the full internet using security group A.
Download particular versions of Java and Python
Remove its internet privileges by removing security group A and adding a security group B.
I observe that there is a DependsOn attribute for specifying the order in which to create resources, but I was unable to find a feature that would allow me to update the security groups on the same EC2 instance twice over the course of creating a stack.
Is this possible with CloudFormation?
Not in CloudFormation natively, but you could launch the EC2 instance with a configured userdata script that itself downloads Java/Python and the awscli, as necessary, and then uses the awscli to switch security groups for the current EC2 instance.
However, if all you need is Java and Python pre-loaded then why not simply create an AMI with them already installed and launch from that AMI?
The best way out is to utilise a Cloudformation custom resource here. You can create a lambda function that does exactly what you need. This lambda function can then be called as a custom resource function in the cloud formation template.
You can pass your new security group ID and instance ID to the lambda function and code the lambda function to use AWS SDK and do the modifications that you need.
I have leveraged it to post an update to my web server about the progress of the cloud formation template. Below is the sample code of the template.
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: /
Roles: [!Ref 'EC2Role']
MarkInstanceProfileComplete:
Type: 'Custom::EC2InstanceProfileDone'
Version: '1.0'
DependsOn: EC2InstanceProfile
Properties:
ServiceToken: !Ref CustomResourceArn
HostURL: !Ref Host
LoginType: !Ref LoginType
SecretId: !Ref SecretId
WorkspaceId: !Ref WorkspaceId
Event: 2
Total: 3
Here the resource MarkInstanceProfileComplete is a custom resource that calls a Lambda function. It takes the event count and total count as input and processes them to calculate percentage progress. Based on that it sends out a request to my web server. For all we care, this Lambda function can do potentially anything you want it to do.

CloudFormation cross-stack vs nested-stack

I'm facing a decision to Use Cross-Stack References to Export Shared Resources or to Use Nested Stacks to Reuse Common Template Patterns following AWS CloudFormation best practices.
However, they seem the same to me apart from a few differences:
cross-stack uses Fn::ImportValue, templates are in one folder.
nested-stack must be in S3, uses type AWS::CloudFormation::Stack and TemplateURL.
There's no clear pros and cons between them as far as I could search.
My goal is to create a parent stack that passes some core variables like stackName to the child stacks, then the child stacks create the resources sharing some variables between them like ARN or Policies, using the stackName to name their resources like stackNameDynamoDBTable.
You should use cross-stack references as it was created for your use case of passing between stacks.
Whereas nested stacks would work, it’s primary purpose is for reuse of modular components, like a template of a resource you use in lots of stacks to save copy pasting and updating the stacks independently.
Nested stacks: if you need to manage your stacks from a single point, you should use nested stacks.
example: assume that you have load balancer configuration that you use for most of your stacks. Instead of copying and pasting the same configurations into your templates you can create a dedicated template for load balancer.
cross-stack : Alternatively, if you need to manage your stacks as separate entities, you should use cross-stack references.(AWS limits the number of VPCs you can create in an AWS region to five.)
example : You might have a network stack that includes a VPC, a security group, and a subnet. You want all public web apps to use these resources. By exporting the resources, you allow all stacks with public web applications to use them.
There is a way to get the best of both worlds. The trick is to use cross-stack resource sharing but make it depend on a parameter that is passed using Nested stack.
Here's an example from how I used this, consider two stacks IAMRoleStack and ComputeStack. The former contains all the necessary IAM roles and the latter contains a bunch of Lambda functions that those roles are applied to.
Resources:
IAMCustomAdminRoleForLambda:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Policies:
Output:
IAMRoleArnForLambda:
Description: Returns the Amazon Resource Name for the newly created IAM Custom
Role for Lambda function
Value: !GetAtt 'IAMCustomAdminRoleForLambda.Arn'
Export:
Name: !Sub '${AWS::StackName}-IAMRoleArnForLambda'
StackName:
Description: Returns name of stack after deployment
Value: !Sub ${AWS::StackName}
As you can see I've exported the IAM role but it's Name depends on the stack name that is calculated once the stack is deployed. You can read more about exporting outputs in the docs.
In the ComputeStack, I use this role by importing it.
Resources:
LambdaForCompute:
Type: AWS::Lambda::Function
Properties:
Role: !ImportValue
Fn::Sub: ${StackNameOfIAMRole}-IAMRoleArnForLambda
The parent stack that "nests" both ComputeStack and IAMRoleStack orchestrates passing the stack name parameter.
Resources:
IAMRoleStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref IAMRoleStackURL
ComputeStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref ComputeStackURL
Parameters:
StackNameOfIAMRole: !GetAtt IAMRoleStack.Outputs.StackName
I can't attest to best practice but this style allows me to pick and choose where I want orchestrated deployment and where I want to do the deployments individually.
I also want to point out that this kind of modularization based on type of resources is not very feasible for nested stacks. For e.g. in this scenario, if I had 10 different roles for 10 different Lambda functions, I would have to pass each of those 10 roles through parameters. Using this hybrid style, I only need to pass one parameter the stack name.
With cross stacks, you pass a reference to a bunch existing components X to stacks A and B when you want A and B to reuse these very same existing components. With nested stacks, when you nest a nested stack Y in stacks C and D, Y shall create a new set of components Y is describing individually for C and for D.
It is similar to concepts 'passing by reference' and 'passing by value' in programming.

How to insert AWS resource IDs into application configuration files

I'm following the AWS guide for deploying an HA Wordpress site to Elastic Beanstalk which includes using the eb-php-wordpress extension. The process requires editing a couple of configuration files with known resource IDs prior to deploying the application.
In particular, the instructions say to edit the efs-create.config file with a VPC ID, and Subnet IDs. The file, among other things, helps set the OptionSettings property of the AWS::ElasticBeanstalk::Environment resource. For this reason, I suspect I should just be able to reference it with Ref:. Is this correct, though since the VPC would be created by another file and the EB environment Cloudformation stack is created next to the VPC stack rather than "inside" it? Would I have to use a Fn:: call to get the information?
The section of the configuration file I'm working with looks like this:
option_settings:
aws:elasticbeanstalk:customoption:
EFSVolumeName: "EB-EFS-Volume"
VPCId: "vpc-XXXXXXXX"
## Subnet Options
SubnetA: "subnet-XXXXXXXX"
SubnetB: "subnet-XXXXXXXX"
SubnetC: "subnet-XXXXXXXX"
SubnetD: "subnet-XXXXXXXX"
Would the VPCId line be something like
VPCId: {Ref: VPC}
Where VPC is the name of the VPC resource that I've created? Or, more simply, how would I reference the VPC ID of the default VPC if I stick with that?
You should be able to use Ref to get the various IDs of the elastic beanstalk named resources, according to the docs. However, the VPC is not one of these named resources (ie those with a logical ID), but is a property of one of the named resources, in this case, the logical ID is AWSEBSecurityGroup and the property is VpcId so you should be able to get it instead using GetAtt:
{ "Fn::GetAtt" : [ "AWSEBSecurityGroup", "VpcId" ] }
from the functions docs and the CloudFormation docs
A similar approach should also work for the subnets.