And we have (Space)lift (off)!
Paweł Piwosz
Posted on February 26, 2023
This time we will configure and run our first small template through Spacelift. 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
-----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.
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 {
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
.
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
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.
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:
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
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)
And configure your AWS integration accordingly.
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.
Posted on February 26, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.