Self-Service AWS Infrastructure using Spacelift

morethancertified

Derek Morgan

Posted on April 11, 2023

Self-Service AWS Infrastructure using Spacelift

Intro

In this article of the Self-Service AWS Infrastructure for Your Devs series, we're going to deploy our VPC and the peered Client VPC using Spacelift and several of its features. This will be the easiest of the methods since state is fully managed, all authentication with Github is managed, Authentication to AWS is simple, and the Blueprints feature provides an excellent self-service interface for your devs with very little effort. We'll deploy the entire setup using a few clicks in the GUI followed by writing everything else in Terraform. Let's get started!

Spacelift as Code

First, we're going to create the code needed to deploy all of the assets. Once we've done that, we'll create the initial Spacelift admin stack and deploy everything. This will all be created as a monorepo in Github, but you can structure it however you see fit if you have other organizational requirements.

The Admin Stack Repository Code

This Terraform code will create the infrastructure stack and the custom Blueprint the developers can use to deploy client VPCs. If you don't name your repository aws-self-service and use all of the same directory names, ensure you modify all references within the code.

# ./administrative/providers.tf

terraform {
  required_providers {
    spacelift = {
      source = "spacelift-io/spacelift"
    }
  }
}

provider "spacelift" {}
Enter fullscreen mode Exit fullscreen mode
# ./administrative/stacks.tf

# this data source will retrieve the stack_id of the admin stack 
# we will create next.
data "spacelift_stack" "admin" {
  stack_id = "admin"
}
# Check the attributes below for your VCS settings
# Learn more about stacks here: 
# https://docs.spacelift.io/concepts/stack/
resource "spacelift_stack" "shared_infra" {

  autodeploy        = false
  branch            = "main"
  project_root      = "shared_infra"
  description       = "Core Infra Stack"
  name              = "shared-infra"
  space_id          = "root"
  repository        = "aws-self-service"
  terraform_version = "1.2.9"
  labels            = ["managed"]
}

# You will create the `dev-context` further in the post. 
# More about contexts here: 
# https://docs.spacelift.io/concepts/configuration/context
resource "spacelift_context_attachment" "attachment" {
  context_id = "dev-context"
  stack_id   = "spacelift_stack.shared_infra.id"
  priority   = 0
}
Enter fullscreen mode Exit fullscreen mode
# ./administrative/blueprints.tf
# More about Blueprints here: 
# https://docs.spacelift.io/concepts/blueprint/

locals {
  bprint = file("${path.root}/blueprints/client_vpc.tftpl")
}

resource "spacelift_blueprint" "client_vpc" {
    name = "Client VPC"
    description = "Stack to create a new child VPC"
    space = "root"
    template = local.bprint
    state = "PUBLISHED"
    labels = ["client"]
}
# for troubleshooting purpose
output "bprint" {
  value = local.bprint
}
Enter fullscreen mode Exit fullscreen mode
# ./administrative/blueprints/client_vpc.tftpl
# inputs are used to create input fields
inputs:
  - id: client_name
    name: Client name  
  - id: vpc_cidr
    name: CIDR of the VPC
    type: select
# You could use a data source here to iterate over a list of 
# available subnets that don't overlap with the main.
    default: 10.1.0.0/16
    options:
      - 10.2.0.0/16
      - 10.3.0.0/16
      - 10.4.0.0/16
  - id: region
    name: Choose AWS region
    type: select
# ensure you set these appropriately
    options:
      - us-east-1
      - us-east-2
  - id: trigger_run
    name: Trigger a run upon stack creation
    type: boolean
    default: false
stack:
  name: ${{ inputs.client_name }}-stack
# More info about Spaces here: 
# https://docs.spacelift.io/concepts/spaces/
  space: root
  description: >
    Stack created from a blueprint by ${{ context.user.name }} logged in as ${{ context.user.login }}
  labels:
    - "blueprints/${{ context.blueprint.name }}"
# Uncomment the vcs section below and add your information.
  vcs:
   branch: main
   repository: aws-self-service
   project_root: client_vpc
   provider: GITHUB
  vendor:
    terraform:
      manage_state: true
# Use your preferred version of Terraform here
      version: "1.4.0"
  attachments:
    contexts:
      - id: dev-context
        priority: 1
  environment:
    variables:
      - name: TF_VAR_client_name
        value: ${{ inputs.client_name }}
      - name: TF_VAR_vpc_cidr
        value: ${{ inputs.vpc_cidr }}
      - name: TF_VAR_region
        value: ${{ inputs.region }}
options:
  trigger_run: ${{ inputs.trigger_run }}
Enter fullscreen mode Exit fullscreen mode

Once you have created all of the code, commit it to your Git repository that contains the code from the first part of this series. If you do not wish to make modifications to VCS settings in the code, make sure you name your repo aws-self-service and your directories the same as what you see in the code snippets above. This is a relatively intermediate article, so I won't go into depth on how to do this. If you have any questions, feel free to reach out.

Creating the Spacelift Admin Stack

Setting up Spacelift is easy. If you don't have an account yet, you can see the "Getting Started" documentation here to get you up to speed quickly: https://docs.spacelift.io/getting-started.

Once you have your account setup, follow the steps below.

1. In the console, click on Add stack

Adding a stack in the Spacelift Console

2. Configure your repository settings

Configuring repository settings in the Spacelift console

3. Customize any settings you need and click continue

Customizing stack settings in the Spacelift console

4. Toggle Administrative and save

Enabling administrative options

Create the AWS Credentials Context

There are multiple ways to provide AWS credentials to our stacks. In Spacelift, you can create a "Cloud Integration" that will assume a temporary role and use it to create resources. This is the preferred route, but in the interest of simplicity and focus, I'm going to pass in the credentials manually. With this method, ensure you rotate your keys frequently and disable them when not in use. Spacelift is a very secure product, but it's always better to be cautious.

What we're going to do is use a "Spacelift Context" to store the keys as an environment variables that can be accessed by any stack to which the Context is attached. To create the new Context, head to the Contexts pane on the left, fill out the necessary information, create the variables, add their values, and designate them as "secret" as shown in the image below. Unless you wish to modify the code above, ensure you use "dev-context" as the name:

Creating a Spacelift context

Let's Deploy!

Once your code is in your repository, the Admin stack is connected to that code, and the Context has been created, it's time to finally deploy! This is going to deploy:

  1. The shared-infra stack that will deploy the shared VPC.
  2. A Spacelift Blueprint that will allow you to enter the information needed to create a stack that will deploy another VPC that will automatically peer to the shared-services VPC.

Once the configuration is finished, go ahead and trigger the Admin stack and let's check it out! You should see your Admin stack and your new shared-infra stack:

Triggering an admin stack in Spacelift

And your new Blueprint:

A Blueprint in the Spacelift console

Once those resources have been deployed, it's time to deploy the shared-resources VPC. Trigger the shared_infra stack to do so and verify the resources were created afterwards:

Deploy the VPC

Once that's complete, your self-service deployment is complete! Head over to the Blueprints tab, fill out the necessary information, and create stack:

Create a stack from a Blueprint in Spacelift

Once the stack is created, you can trigger it and you'll have your very own self-service VPC!

See your new deployed VPC

💖 💪 🙅 🚩
morethancertified
Derek Morgan

Posted on April 11, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related