/terraform-starter

Starter repository to play with Spacelift

Primary LanguageHCLMIT LicenseMIT

So you want to take Spacelift for a spin?

This repository is designed just for this purpose. So, fork it to a GitHub account you manage (either your private account or an organization you have admin rights on) and let's get started.

In this tutorial you will not be using any cloud providers, you won't need any extra credentials and the only resources you will manage are the ones managed by Spacelift's own Terraform provider.

Step 1: Installing GitHub application

Provisioning a Spacelift account is fully automated and only involves installing the GitHub app. At this point it's up to you to decide whether to give Spacelift access to all your repositories...

Installing Spacelift for all repositories

...or just the selected ones:

Installing Spacelift for selected repositories

Note: don't agonize over this choice - you can always change it later.

Installing the application takes you to your first Spacelift screen where you can create your first stack.

Step 2: Creating your first stack

Stacks are probably the most important concept in Spacelift. They connect your code and your infra, with some configuration in-between. To keep things short, a Spacelift stack maps directly to a single Terraform state file.

VCS integration

So without further ado, let's go through the stack creation process step by step. First, we need to tell Spacelift where the project code lives - this is the Integrate VCS step. Since in the previous step I only gave Spacelift access to a single repository (safety first), and this repository only has a single branch, my choices here are rather... limited. But you get the idea - you select the repo, you select the default branch to deploy from and off you go:

Integrating VCS

Stack behavior

In the next step you will define some things about this stack's behavior. Since this is meant to be a quick and snappy tutorial we won't go into the details, but you can read more about them here. For now the only tweak we need to do here is to mark the stack as administrative. Why? Because only administrative stacks can manage Spacelift resources and that's what we'll be creating as part of this lab.

Defining behavior

Managing state

The next step is all about managing Terraform state. Spacelift can work with any remote state provider but it also provides its own state backend - though beyond convenience there are no benefits to using it. We don't have an existing state to import, so we do the most convenient thing there is - continue the defaults:

Configuring state

Naming the stack

As they say, there are 2 hard problems in computer science: cache invalidation, naming things, and off-by-1 errors. We'll make it easy this time: we've come up with a good name for your first stack, so feel free copy it. For now we won't care about labels or description (yes, we support Markdown), though you can read up on them when you're done with this lab.

Naming stack

Congrats, you have your first stack. It doesn't do much, but we'll change it in the next step.

Step 3: Triggering a run

Creating a stack takes you to a very sad looking screen:

Trigger run

Let's add some color here. We can do that by clicking the Trigger button in the upper right hand corner of the screen. The trigger button will create a Spacelift job that will check out you code, run the usual Terraform commands on it and present you with the choice on whether you want to apply them or not:

Confirm or discard

You can always refer to the logs directly to see what's changing. In this case, we're creating 24 Spacelift resources, and they all look good. So let's confirm and see what happens next:

Changes applied

Wow, 6 seconds? That was quick! Let's go back to our main (Stacks) screen to see what we've just done.

Step 4: Exploring created resources

What we just did in Step 3 was creating a bunch of very useful Spacelift resources. Looking at the main screen we can quickly notice two things - our Terraform starter stack turned green (always a good sign!) and there's another Stack we haven't seen before, called Managed stack:

New stack

Environment

Now where did that come from? In fact, we had it declared it using Terraform, just here. The same file defines a bunch of things related to the environment, so let's click on the name of the new stack to be taken to its screen. Since it doesn't contain anything interesting just yet, let's quickly navigate to the Environment screen. And it's indeed a very busy screen, so let's just look at the first section there:

Environment

What we see is a bunch of environment variables and mounted files - some public and some secret - that we indeed saw defined in the stack.tf we've only just looked at. But there are others - see the ones with the blue label to their right? Where did they come from? They're actually defined here and they belong to a context that's attached to the new Stack.

But before we move on to the context, click the Edit button in the upper right hand corner of the screen and play around with environment variables and mounted files.

Context

Contexts are how Spacelift does configuration reuse. Rather than having to copy and paste a bunch of configuration variables, Spacelift allows you to encapsulate them as a package and attach them to as many stacks as you want. So if you navigate back to the main screen (hint: click on the logo, it normally works like that single button on your iPhone) and then go to the Contexts screen, that's what you're going to see:

Contexts

Clicking on the context name takes you to the context screen, where you can see that it currently contains two environment variables (one plaintext and one secret) and two mounted files, one plaintext and one secret:

Context

Note that you can edit the context just as you can edit the Stack's own environment. That Edit button is there for a reason, so go wild for a while before we move on to policies, which are the second most important topic in Spacelift.

Policies

When you navigate to the Policies screen, you will see that we've created 6 different policies for you - one of each available types except for the initialization policy, which would take a bit longer to explain and thus is not in the scope of this short tutorial.

Policies

All these policies are defined and explained in the policies.tf file, but let's go through them one by one:

  • DevOps are admins is a login policy that would make anyone who's a member of the DevOps team in your GitHub organization to log in as a Spacelift administrator as opposed to the default situation where only GitHub admin users are automatically Spacelift admins. Note: this changes if you're using SSO instead of GitHub to authenticate;

  • All of Engineering gets read access is an access policy that gives any member of the Engineering GitHub team read access to every stack it's attached to;

  • Ignore commits outside the project root is a Git push policy that ignores push notifications which do not affect any files outside of project root of the stack it's attached to;

  • Enforce password strength is a plan policy that prevents you from creating weak passwords using random_password resource type - we'll see this one in action really soon;

  • Allow only safe commands is a task policy that only allows you to run certain commands as tasks. This is another one that we're going to try hands-on;

  • Trigger stacks that declare an explicit dependency is a trigger policy that will cause every stack that declares dependency to be triggered when the current one is updated - while this one is probably beyond the scope of the basic tutorial, we wanted to show you that Spacelift is Turing-complete 😜

Policies in practice

While it's worth mentioning that we're using an open-source language for our policies, authoring policies is outside of the scope of this tutorial. Instead, we'd like to show you the power of policies hands-on. Let's then navigate to our new stack (Managed stack) and trigger a run. Oh no, what did just happen? Looks like what we're trying to does not agree with our policy:

Plan policy failing a run

Don't worry for now about fixing it, we will do that in the next step. Instead, let's navigate to our new Stack's Tasks screen and try to run something. How about terraform destroy -auto-approve? Nope, didn't work either:

Task failed

Hint: try running ls. Figuring out why it succeeds is an exercise left to the reader.

Step 5: tests and pull requests

In this step we'll try to fix the problem reported by the plan policy and while doing that we'll see how Spacelift deals with testing your changes and handling Pull Requests. So without further ado, let's make a change to our code to make the random password we're trying to create in this new stack just a little bit longer - let's say 24 characters. Let's also not merge that change to main but create a separate branch and open a Pull Request:

Open Pull Request

And here's the exact change we're making:

Pull Request changes

That little change causes two runs to be executed since this repo is now connected to two stacks - one that created manually and one that is managed programmatically. It's the latter stack we've made changes to, so you will see that there are no changes to the former but one resource would be created for the latter. Note that we report the same information in two ways - one (Atlantis-like) is a PR comment and the other one - commit status checks. The good thing about the latter is GitHub allows you to block PR merges on failing commit status checks.

Pull Request feedback

Clicking on the Details link next to the commit status check takes you to the test run for the affected stack. Let's click on that and see what gives:

Test run

Now that the push policy is happy with the new length of your password, you can merge the Pull Request to the main branch. A run will be created automatically in the Runs tab of your Managed stack which should apply the changes automatically (see the autoapply setting in stack.tf):

Run from a merged PR

Congratulations! 👏🏻

You're a Spacelift expert now 😂 If you like what you've seen so far, here's a few next steps we can suggest: