Creating multiple stack instances

This section walks through an example stack project that has multiple environments.

The example source code

The example project is in the folder examples/02-multiple-stacks, available from github at https://github.com/cloudspinners/cloudspin-reference/tree/master/02-environments.

This project creates some networking structures, just like the 01-basic-stack project, although it adds an EnvironmentName tag to the resources.

Environment configuration options

This project adds some new parameters to the stack-instance-defaults.yaml file:

instance:
  group: NOT_SET

parameters:
  environment_name: NOT_SET
  region: eu-west-1
  availability_zones: "eu-west-1a,eu-west-1b,eu-west-1c"

parameters:environment_name is passed to terraform, so it can be assigned to resource tags. This is purely a convention - nothing in cloudspin itself insists on this.

instance:group is used by the cloudspin tools to build unique a instance_identifier for each stack instance. Before, the instance_identifier was set to STACK_NAME, which was taken from the stack:name options set in the stack definition file ./src/stack-definition.yaml. Now, instance:group is added to it, so the the instance_identifier is set to STACK_NAME-INSTANCE_GROUP.

Create your local stack instance configuration

As with the previous tutorial, you need to create a local configuration file so the cloudspin tools can access your AWS account. You can copy the example configuration file:

$ cp stack-instance-local-example.yaml stack-instance-local.yaml

And then edit the file to match your account details.

"Local" stack instance

You can use the stack command as in the previous tutorial to manage your own instance of the stack.

$ stack up --dry

The output should look something like:

cd /YOUR_PROJECT_FOLDER/02-environments/work/example2-NOT_SET && terraform init -backend=true -backend-config 'region=eu-west-1' -backend-config 'encrypt=true' -backend-config 'bucket=statebucket-cloudspin-examples-xxxxxxxxxxxx' -backend-config 'profile=YOUR_UNPRIVILEGED_PROFILE' -backend-config 'role_arn=arn:aws:iam::xxxxxxxxxxxx:role/spin_role-cloudspin_examples-manager' -backend-config 'key=example2-NOT_SET.tfstate'
cd /YOUR_PROJECT_FOLDER/02-environments/work/example2-NOT_SET && terraform apply -var 'region=eu-west-1' -var 'availability_zones=eu-west-1a,eu-west-1b,eu-west-1c' -var 'environment_name=NOT_SET' -var 'aws_profile=YOUR_UNPRIVILEGED_PROFILE' -var 'assume_role_profile=assume-cloudspin_examples-manager' -var 'assume_role_arn=arn:aws:iam::xxxxxxxxxxxx:role/spin_role-cloudspin_examples-manager' -var 'instance_identifier=example2-NOT_SET'

The interesting bit here is the environment_name variable: environment_name=NOT_SET, and the instance_identifier variable: instance_identifier=example2-NOT_SET.

The stack-instance-defaults.yaml file, as shown above, defines both the environment and group parameters to be NOT_SET. In order to have multiple stack instances, we’ll need a way to override these.

You can run stack down to destroy this stack instance.

Environment configuration files

Configuration options for different environments can be set by creating files in a folder called ./environments, each file being named stack-instance-ENVIRONMENT_NAME.yaml.

instance:
  group: sandbox

parameters:
  environment_name: sandbox

This example overrides the instance:group value and the environment_name parameter, but you could override other variables as well.

To use this with the stack tool, use the -e flag to pass the ENVIRONMENT_NAME to the tool:

$ stack up --dry -e sandbox

The stack tool uses the environment string passed to it to find a file named ./environments/stack-instance-ENVIRONMENT.yaml, which in this case is: ./environments/stack-instance-sandbox.yaml. The output is something like:

cd /YOUR_PROJECT_FOLDER/02-environments/work/example2-sandbox && terraform init -backend=true -backend-config 'region=eu-west-1' -backend-config 'encrypt=true' -backend-config 'bucket=statebucket-cloudspin-examples-xxxxxxxxxxxx' -backend-config 'profile=YOUR_UNPRIVILEGED_PROFILE' -backend-config 'role_arn=arn:aws:iam::xxxxxxxxxxxx:role/spin_role-cloudspin_examples-manager' -backend-config 'key=example2-sandbox.tfstate'
cd /YOUR_PROJECT_FOLDER/02-environments/work/example2-sandbox && terraform apply -var 'region=eu-west-1' -var 'availability_zones=eu-west-1a,eu-west-1b,eu-west-1c' -var 'environment_name=sandbox' -var 'aws_profile=YOUR_UNPRIVILEGED_PROFILE' -var 'assume_role_profile=assume-cloudspin_examples-manager' -var 'assume_role_arn=arn:aws:iam::xxxxxxxxxxxx:role/spin_role-cloudspin_examples-manager' -var 'instance_identifier=example2-sandbox'

So you can see that the environment_name variable passed to terraform is now environment_name=sandbox, and instance_identifier is now instance_identifier=example2-sandbox.

Create multiple instances of the stack

There are environment configuration files for two environments, sandbox and staging. You can spin up an instance of each:

stack up -e sandbox
stack up -e staging

You can then see the VPCs in the AWS console, or by running the aws cli:

aws ec2 describe-vpcs --profile assume-cloudspin_examples-manager --region eu-west-1

Rakefiles and multiple stack instances

You can create multiple StackTask instances in your rakefile, passing an environment string to each one. Like the stack command line tool, the stack rake task will search for the environment configuration file using this string, and use that to configure the stack instance.

This example iterates over each environment name in an array, creating a namespace for each:

require 'cloudspin/stack/rake'
include Cloudspin::Stack::Rake

['sandbox', 'staging'].each { |environment|
  namespace "#{environment}" do
    stack = StackTask.new(environment).instance
    InspecTask.new(stack_instance: stack)
  end
}

So the rake tasks are grouped by a namespace for each environment:

$ rake -T
rake sandbox:down    # Destroy stack instance
rake sandbox:dry     # Show command line to be run for stack instance
rake sandbox:inspec  # Run inspec tests
rake sandbox:plan    # Plan changes to stack instance
rake sandbox:up      # Create or update stack instance
rake staging:down    # Destroy stack instance
rake staging:dry     # Show command line to be run for stack instance
rake staging:inspec  # Run inspec tests
rake staging:plan    # Plan changes to stack instance
rake staging:up      # Create or update stack instance

Testing

The testing story is pretty much the same. The StackTask class automatically adds inspec tasks for each stack instance, as long as it finds inspec controls in the ./test folder. The inspec control code itself needs to use the instance_identifier attribute to ensure it is testing the correct instance:

instance_identifier = attribute('instance_identifier', description: 'Which stack to test')

describe aws_vpc_list do
  its('name') { should include "vpc-#{instance_identifier}" }
end

Cleaning up

stack down -e sandbox
stack down -e staging

Doing more interesting things

The main thing we’ve accomplished in this tutorial is the ability to create more than one stack instance from a single stack definition. The only difference with each instance is the two variables, instance_identifier and environment_name, which only affect tagging. However, you can see how it would be possible to set other variables differently per environment. This would require adding the variables to the environment configuration files, and then adding the variables within the Terraform code to make use of these.

results matching ""

    No results matching ""