And we have (Space)lift (off)!
Tags: spacelift, aws, configuration, tutorial
Published: 2023-02-26 21:40:14

---
title: And we have (Space)lift (off)!
series: spacelift
published: true
description: Get started with Spacelift
tags: #spacelift #aws #configuration #tutorial
cover_image: https://www.pawelpiwosz.net/assets/covers/spacelift-cover.png
---

This time we will configure and run our first small template through [Spacelift](https://spacelift.io/). Our first step will be to configure the connection between AWS and the service. I assume, that the Spacelift account is created and connected with GitHub.

In this short series we will learn how to configure, connect and start to use Spacelift.

First thing to do for each real developer is to switch the GUI to dark mode :D Go to your account settings on the bottom left corner of your screen and find the `dark mode` setting. Right, we are ready to go :)

# AWS

## Connect Spacelift with Cloud provider

To work with AWS we need to configure our connection to the vendor. The documentation provided by Spacelift is really rich and clear, however, I'd like to mention the security aspect. First way, the easier one, is to provide programmatically available user with role to assume. Is it a good solution? Well, good enough. However, I prefer to use another way, which also is provided by Spacelift - the OIDC connection. On the end of the day it does the same thing, but from the security standpoint - it is better.

OIDC needs some configuration (obviously) and part of it is a thumbprint. As I do this as a tutorial, not fully blown production ready stuff, I show you how to get this thumbprint using CLI approach.

First, let's collect url of our spacelift app. Simply, look on your browser :) In my case it it `https://<subdomain>.app.spacelift.io/`. We have it, so let's generate the thumbprint.

I use Ubuntu to get these information. In fact WSL2 on Windows :). Execute:

`curl https://<subdomain>.app.spacelift.io/.well-known/openid-configuration|jq`

Please note, I added `jq` on the end to have nicer output.

Find the line with `jwks_uri`. Copy from it the domain name **only** and use it in following command.

`openssl s_client -servername <subdomain>.app.spacelift.io -showcerts -connect <subdomain>.app.spacelift.io:443`

Ensure, you don't have https, etc. Just the domain.

Scroll the output and find the certificate. There will be something like

```text
-----BEGIN CERTIFICATE-----
somestring
-----END CERTIFICATE-----
```

Create a file (for example `certificate.crt`) and copy this whole part there.

Now we are ready to generate thumbprint

`openssl x509 -in certificate.crt -fingerprint -sha1 -noout |tr -d :`

As you can see I used the `tr` command with pipe to get rid of `:`. If you are not familiar with pipes and redirections in Linux, no worries, [here is my lab about it](https://killercoda.com/pawelpiwosz/course/linuxFundamentals/lf-05-pipes).

The string in output is a part which we need to use to complete our configuration of OIDC.

> We can do it with Terraform, of course. In fact, if you plan to use it in the real project, I strongly recommend to do it with IaC. However, now you know how to do it from CLI :)

## Let's terraform it

You know what? Creating all these resources on AWS from GUI is so old-fashioned :) Let's have a small Terraform template for it! We need to create:

* OIDC itself
* IAM Role to assume by Spacelift
* IAM Policy which describes what Spacelift can do.

First, let's create the file `providers.tf` with this content

```terraform
terraform {

  required_version = ">=1.3"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}
```

And we can forget about this file from now.

We know what is our thumbprint, so we can create first block in `main.tf`.

```terraform
provider "aws" {
  default_tags {
    tags = {
      Environment = "Sandbox"
      Terraform   = "True"
      Repo        = "spacelift-prep"
      Project     = "Spacelift tutorial"
    }
  }
}

resource "aws_iam_openid_connect_provider" "spacelift" {
  url = "https://<subdomain>.app.spacelift.io"

  client_id_list = [
    "<subdomain>.app.spacelift.io",
  ]

  thumbprint_list = ["<thumbprint>"]
}
```

Please note, we also have the provider defined here.

Now, it is time to define the IAM Role. Within this definition, we will ensure that the Role can be assumend by Spacelift only. We want to build the trusted relation between this entity and Spacelift to secure our connection as much as possible.

The Role and condition inside is described well in Spacelift's documentation, so, I will not go into details. I just explain a few parts.

First, we use Federated access and we use for it the OIDC we defined earlier.

Second, please note the `Condition` section of the Role. This makes the connection more secure by narrowing the entities which can use this role. We can create even more precise boundary, all is in documentation. However, this one is enough for us at this moment.

And here is the Terraform code for our Role

```terraform
resource "aws_iam_role" "spacelift-role" {
  depends_on = [
    aws_iam_openid_connect_provider.spacelift
  ]

  name        = "spacelift-role"
  description = "Role to assume by spacelift"

  assume_role_policy = <<ROLE
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "${aws_iam_openid_connect_provider.spacelift.arn}"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "<subdomain>.app.spacelift.io:aud": "<subdomain>.app.spacelift.io"
        }
      }
    }
  ]
}
ROLE
}
```

You might ask, *why do you use this old-fashioned way with `<<ROLE`?*. Well, good question :) For two reasons. I learned Terraform this way and this approach helps me to better see where Role or Policy document ends. Quite handy, especially for long documents.

Ok, finally, we will create "very secure" Policy and we will attach Policy to the Role. Please have in mind, that the Policy should by tailored to needs, not open like here. We do it for demo purposes, so we can live with it now.

```terraform
resource "aws_iam_policy" "spacelift-policy" {
  name = "spacelift-policy"

  policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "*",
      "Resource": ["*"]
    }
  ]
}
POLICY
}

resource "aws_iam_role_policy_attachment" "spacelift-iam-attachment" {
  role       = aws_iam_role.spacelift-role.name
  policy_arn = aws_iam_policy.spacelift-policy.arn
}
```

No magic here, I suppose.

## Init

We have our template, we can execute it.

First, we need to initialize Terraform

`terraform init`

We can format and validate the template

`terraform fmt`

`terraform validate`

Well, I have to do one more thing. I use many AWS profiles, therefore I need to specify it. There are many ways to do so, I use the easier and less flexible one - I put it into template:

```terraform
provider "aws" {
  profile = "demos"
```

Please remember, this is for demo purposes only, so, no issue with it.

## Execute Terraform

We can deploy our stack.

First, let's see if all is ok

`terraform plan`

If all went ok, you should see something like

`Plan: 4 to add, 0 to change, 0 to destroy.`

Ok, so let's rock!

`terraform apply -auto-approve`

This `Apply complete! Resources: 4 added, 0 changed, 0 destroyed.` means success.

But wait... What Role should I use later? We can go to the GUI and... **NO!**

## Outputs

Let's create one more file, called `outputs.tf` and put there

```terraform
output "IAM-Role-to-assume" {
  description = "IAM Role to assume by Spacelift"
  value       = aws_iam_role.spacelift-role.arn
}
```

Now we can run `terraform refresh` and we already see the ARN of the Role. If this info is needed later, we can run `terraform output`.

# Spacelift

Well, looks like this episode is more about Terraform than Spacelift :) It is important though, to have good connection created, so I believe this is not a big deal :)

Ok, let's do our work on Spacelift side!

Go to your dashboard, to the `Cloud integrations (bottom left of the screen)

![Add integration](https://www.pawelpiwosz.net/assets/spacelift-intro/spacelift-intro-01.jpg)

And configure your AWS integration accordingly.

![Configure integration](https://www.pawelpiwosz.net/assets/spacelift-intro/spacelift-intro-02.jpg)

And... Yes, that's it :)

# Key takeways

For now, we know how to connect the dots. In the next episode we will learn what Spacelift is and how to start with it.