Pick up infrastructure as code tools and techniques
Walk through an infrastructure as code template, using AWS CloudFormation as an example, to better understand defining and connecting resources.
Teams embracing a DevOps approach need to learn how to apply software development practices to infrastructure. The transition typically starts with learning to use configuration management tools to apply settings and install applications on server operating systems. Next step: Define all of your infrastructure as code.
Defining infrastructure as code (IaC) helps to automate everything in production IT and put documented application and infrastructure source code in a version control repository.
Infrastructure as code tools often use a declarative JSON-based templating system, putting virtual machines, network configurations and the rest of the infrastructure in a human-readable guide document to reliably and repeatedly build environments with ease.
Start with the basics
Infrastructure as code is no place for diving in at the deep end. Before you explore the depths of resources, properties and dependencies, ensure that the infrastructure as code concept is crystal clear with this primer.
When it comes to doing DevOps in the cloud, Amazon Web Services is a commonly used platform, and its CloudFormation product is the infrastructure as code tool of choice for some of the biggest companies in the world. With a walk through the basics of the CloudFormation template, you'll gain an in-depth understanding of infrastructure as code best practices.
One infrastructure as code tool's structures
AWS CloudFormation templates can build a single instance running an application, or deploy a sophisticated environment comprised of many resources from various AWS cloud services. In either case, the set of resources deployed using a template is referred to as a stack, because templates are often used to build layers of resources that make up an entire application.
The basic structure of a CloudFormation template contains several sections where the user defines resources, specifies conditions, accepts values for input using parameters and completes other customizations. When creating a JSON template, you encapsulate a list of key/value pairs within curly brackets.
The example in Listing 1 shows the major sections and template structure for the AWS infrastructure as code tool. Most of the sections shown in Listing 1 are optional:
- AWSTemplateFormatVersion: The template format is typically included as an infrastructure as code best practice, but it's an optional field. This section refers to the CloudFormation template version which -- as you can tell by the date -- does not change often.
- Description: Optionally, provide a description for the template. If you launch the template in the web-based CloudFormation console, it will render the value used here.
- Metadata: The administrator can also add metadata, if desired, to enable tracking.
- Parameters: Define an optional collection of one or more parameters than can accept input at runtime. Parameters are a great way to eliminate hard-coded values in a template. Operators can provide the template values when they launch a new stack.
- Mappings: These sections are also optional; mappings create lookup tables. For example, one common use for mappings is to define Amazon Machine Image (AMI) IDs. If your template needs to work in multiple AWS regions, you can create a mapping for your AMI with the unique AMI ID in each region.
- Resources: This is the only required section; declare all AWS resources you want to deploy in this section.
- Conditions: Templates typically use static declarations for resources, but there is some basic support for adding conditional logic. For example, you may not want to create certain resources based on the value of one of the input parameters. You could use a condition to do that. This section is also optional.
- Outputs: Consider declaring outputs that operators can use after launching a template. For example, when coding the infrastructure, output the URL for an elastic load balancer so the operator can test a web application after the stack launch is complete.
Declaring resources and properties
Each resource that you declare on an IaC template will have a number of properties you can use to define its configuration. For CloudFormation, these can be AWS products such as Elastic Compute Cloud (EC2) instances, Simple Storage Service buckets, Elastic Load Balancers, Virtual Private Clouds (VPCs) or any other resource from CloudFormation's supported services. Listing 2 shows the addition of a WebServer resource to the Resources section of the JSON template.
This example only includes a few key properties to get a usable instance up and running. The name of this resource is simply WebServer -- use any name that makes sense as long as it is unique within the template.
Within the resource, we have the type name, shown in this example as AWS::EC2::Instance. This tells CloudFormation what type of resource to create. Other properties, such as the ImageID, InstanceType and KeyName define the chosen operating system, the class of virtual machine it should run on, and the private key pair name that the operator uses to connect to the instance once it's online.
Many other properties can be included with EC2 instance resources, such as user data scripts or other initialization metadata for advanced bootstrapping scenarios.
Accepting input through parameters
In some scenarios the infrastructure as code tooling should eliminate static entries in templates. For example, without static entries, the operator of the template must select an instance type at runtime -- when they are launching a stack. The key to making this work is creating a parameter, like the one shown in Listing 3.
The template example now includes a single parameter object called InstanceType. Like the resource added in Listing 2, the parameter can have any name as long as it's unique. The parameter object has a collection of allowed values the user can choose from, along with a default value.
Since there's a default value, the parameter is optional. If the operator doesn't provide a value, then the code defaults to t2.micro. Without a default, the operator must choose a value from the allowed values collection. When CloudFormation templates are launched using the web-based console, the AllowedValues property is rendered as a drop-down list.
The programmer can add multiple parameters to the Parameters section. Consider options such as minimum and maximum length values for strings, maximum values for numbers or allowed patterns based on regular expressions.
Referencing other resources
In addition to declaring resources, the CloudFormation IaC tool supports a number of useful functions. For example, a function called Ref, as shown in Listing 4, can reference the value assigned to a parameter.
In this example, the InstanceType property within the EC2 instance resource is no longer hard coded. Instead of a literal string, the Ref function references the value of the resource named InstanceType. In this case, that resource is a parameter called InstanceType. The default, or a selected value, is used.
There's also a hard-coded value for the ImageId property within the EC2 instance. For templates that need to support multiple regions and multiple AMI IDs, reference a value from a mapping table using a function called FindInMap. This requires a bit more work, but AWS has a great example of how to use this technique in the CloudFormation documentation.
Dependency management
In most cases, infrastructure as code tools have the intelligence to handle dependencies autonomously. For example, if you declare an Amazon VPC and an EC2 instance that will be deployed into one of the VPC subnets, the CloudFormation service understands that it must first create the VPC before it can create the EC2 instance. However, there will likely be times where you need to override the order in which resources are created.
For example, if you declare two EC2 instances in your template, the CloudFormation service will deploy them in parallel by default. To override that, use the DependsOn attribute, as shown in Listing 5.
The code snippet in Listing 5 shows two resources: an EC2 instance called WebServer1 -- its properties are truncated for readability -- and another instance called WebServer2.
The WebServer2 resource has its DependsOn property set to WebServer1. This tells the CloudFormation service to first provision the WebServer1 resource. When that's complete, the service will move on and provision the WebServer2 resource.
Got the basic properties of AWS's infrastructure as code tool down? Keep going, with insights into security and management controls, configuration and more on CloudFormation. And check out documentation and starter templates provided by AWS to embrace a DevOps approach.