DevOps is more than just about developers

There. It has been said. Take a moment, and think about it.

There is a push in the community to talk about Continuous Integration / Continuous Deployment (CI/CD), and tying this all to the DevOps movement. About what practices are used to get code to market the fastest, and with minimal amount of code defects. And, I will admit, that I agree wholeheartedly, that CI/CD or some sort of pipeline is important to allow software development teams to move fast and innovate. Or, to just roll out code without doing it every 6 months. I would almost argue that CI/CD is more akin to Agile and Scrum methodologies than it is to DevOps.

When it comes to DevOps, there is a side to this equation that is not commonly being addressed, Infrastructure and Operations.

Infrastructure Ops

InfraOps, whatever you want to call it. This is the glue that holds together all the services that applications run on top of. It is the items that people take for granted when it works, but cry foul when things go down. When servers go down at 2 a.m. InfraOps is what keeps the lights on.

It is easy to dismiss this side of the DevOps house. But, lets quickly discuss all the items that we need to cover here.

Infrastructure Operations List

  • System Deployment
    • Operating systems
    • Patching
  • Virtualization (Docker, AWS, Azure)
  • CMDB
  • Monitoring
  • Software Deployment
    • 3rd Part
    • In house
  • Load Balancers
  • Hardware Failover
  • Disaster Recovery
  • Caching Servers
  • Database Systems
  • Development Environments
  • Backups

This is by no means an exhaustive list of all the items that must be handled for a solution to be properly deployed and supported. But, the idea is to bring to light the side of the DevOps world that is often overlooked. It does not matter how good your code is if you don’t have an infrastructure that will stay online and support your applications. Often this is an overlooked and under staffed area that needs more attention.

Automation is king

The only way to ensure that the Infrastructure is able to support ongoing systems, deployments, and updates is with automation. Every time that a machine is built by hand, a configuration file is manually changed, or updates are performed manually, you have introduced risk into your environment. Each of these interactions is potentially a slow down in the pipeline, or worse, a manual change that has never been documented.

There are a number of tools and processes wrapped around handling Infrastructure Automation: Chef, Ansible, cfEgine, Salt. The list goes on. In some places there people have rolled their own. It does not matter as much about the tool, as long as we are moving away from manual intervention to a more dynamic scalable infrastructure. These tools all require an in-depth knowledge of underlying systems as well as the ability to code. But the end goal is DevOps on the infrastructure as well as on the Development side of the house.

There are places where tools are lacking to help support of automation. While new SaaS Solutions are filling some of these gaps, if you need to run your monitoring system in house, many of the solutions that currently exist are not built around dynamic infrastructure and ephemeral machines. The tools need to catch up with the needs of the users. In the meantime, the back end DevOps guys write workarounds to handle situations like this.

Moving Forward

Moving forward, let us try and look at all aspects of DevOps, from development to application deployment, and from hardware to infrastructure and scaling design. There is still much work to be done, but we are moving in the correct direction.

Building a Windows 2012R2 Instance in AWS with Terraform

Terraform is an application by HashiCorp that is designed to treat infrastructure as code. Lately, I have been working with it to begin automation of resources within AWS, and have been quite pleased.

Lets get started with building out a Windows 2012 R2 server with Terraform on AWS.

You are going to need to have the following items configured in AWS in order for this to work, as I am not going to be using Terraform to build out these items.

Since the purpose of this test is not to create a VPC, subnets, security_groups, etc, all of those will need to be created beforehand. You will then need the identifiers from these later, for use in building out the server. I always recommend building out your VPC in its own stack, and never mixing it with others. It is a vital piece of the infrastructure that should be touched as little as possible.

Items Needed for this Demo:

  • IAM Instance Role
  • VPC Security Group ID
  • Subnet ID
  • Key to use for Instance Creation

For this example you are going to need just two files. These files are the variables.tf and main.tf. Technically, you can name them any good old name you want, as long as they end in .tf. I almost think it is more confusing to go with main.tf and variables.tf. As such, I am going to go ahead and change the name of the files. I use Atom for editing my files, and so descriptive filenames are nice.

mkdir stand_alone_windows_2012
cd stand_alone_windows_2012
touch win2012_test_instance.tf
touch win2012_test_variables.tf

Now let us add the file that we are going to need for the variables. (I will admit that there are some values hard coded into the second file, but that is because I have been testing this to get it working.

variable "admin_password" {
  description = "Windows Administrator password to login as."
}

variable "aws_region" {
  description = "AWS region to launch servers."
  default = "us-west-2"
}

# Windows Server 2012 R2 Base
variable "aws_amis" {
  default = {
    us-east-1 = "ami-3f0c4628"
    us-west-2 = "ami-b871aad8"
  }
}

variable "key_name" {
  description = "Name of the SSH keypair to use in AWS."
  default = {
    "us-east-1" = "AWS Keypair"
    "us-west-2" = "AWS Keypair"
  }
}

variable "aws_instance_type" {
  default = "m4.large"
}

variable "aws_subnet_id" {
  default = {
    "us-east-1" = "subnet-xxxxxxxx"
    "us-west-2" = "subnet-xxxxxxxx"
  }
}

variable "aws_security_group" {
  default = {
    "us-east-1" = "sg-xxxxxxxx"
    "us-west-2" = "sg-xxxxxxxx"
  }
}

variable "node_name" {
  default = "not_used"
}

You will need to go through this file, and update the variables as needed, and create any resources that you do not happen to have. This would include subnets and vpc based security groups.

The next file is win2012_test_instance.tf. This is what does all the heavy lifting. In my example, I am also installing chef, but that is because I plan on automating my entire infrastructure, not just server creation.

# Specify the provider and access details
provider "aws" {
  region = "${var.aws_region}"
}

data "template_file" "init" {
    /*template = "${file("user_data")}"*/
    template = <
  winrm quickconfig -q & winrm set winrm/config/winrs @{MaxMemoryPerShellMB="300"} & winrm set winrm/config @{MaxTimeoutms="1800000"} & winrm set winrm/config/service @{AllowUnencrypted="true"} & winrm set winrm/config/service/auth @{Basic="true"}


  netsh advfirewall firewall add rule name="WinRM in" protocol=TCP dir=in profile=any localport=5985 remoteip=any localip=any action=allow
  $admin = [ADSI]("WinNT://./administrator, user")
  $admin.SetPassword("${var.admin_password}")
  iwr -useb https://omnitruck.chef.io/install.ps1 | iex; install -project chefdk -channel stable -version 0.16.28

EOF

    vars {
      admin_password = "${var.admin_password}"
    }
}

resource "aws_instance" "win2012_instance" {
  connection {
    type = "winrm"
    user = "Administrator"
    password = "${var.admin_password}"
  }
  instance_type = "${var.aws_instance_type}"
  ami = "${lookup(var.aws_amis, var.aws_region)}"
  key_name = "${var.key_name}"
  tags {
    Name = "MY_DYNAMIC_STATIC_NAME",
    Env = "TEST"
  }
  key_name = "${lookup(var.key_name, var.aws_region)}"
  iam_instance_profile = "STATIC_ROLE_NAME_SHOULD_BE_A_VARIABLE"
  tenancy = "dedicated"
  subnet_id = "${lookup(var.aws_subnet_id, var.aws_region)}"
  vpc_security_group_ids = ["${lookup(var.aws_security_group, var.aws_region)}"]
  /*user_data = "${file("user_data")}"*/
  user_data = "${data.template_file.init.rendered}"
}