poc: terranix

This commit is contained in:
Kiara Grouwstra 2024-01-13 16:31:46 +01:00
parent cda93dc555
commit 67ec8d23f6
15 changed files with 320 additions and 293 deletions

1
.envrc Normal file
View File

@ -0,0 +1 @@
use flake

6
.gitignore vendored
View File

@ -1,3 +1,6 @@
# Direnv directory
.direnv/
# Local .terraform directories
**/.terraform/*
@ -15,3 +18,6 @@ terraform.rc
# Ignore local .tfvars
*.tfvars
# generated terraform files
*.json

View File

@ -1,53 +0,0 @@
image: registry.gitlab.com/gitlab-org/terraform-images/stable:latest
variables:
TF_ROOT: ${CI_PROJECT_DIR}
TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/bij1
cache:
key: bij1
paths:
- ${TF_ROOT}/.terraform
before_script:
- cd ${TF_ROOT}
stages:
- prepare
- validate
- build
- deploy
init:
stage: prepare
script:
- gitlab-terraform init
validate:
stage: validate
script:
- gitlab-terraform validate
plan:
stage: build
script:
- gitlab-terraform plan
- gitlab-terraform plan-json
artifacts:
name: plan
paths:
- ${TF_ROOT}/plan.cache
reports:
terraform: ${TF_ROOT}/plan.json
apply:
stage: deploy
environment:
name: production
script:
- gitlab-terraform apply
dependencies:
- plan
when: manual
only:
- master

View File

@ -1,10 +1,19 @@
# This file is maintained automatically by "terraform init".
# This file is maintained automatically by "tofu init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/digitalocean/digitalocean" {
version = "2.21.0"
constraints = "~> 2.21.0"
provider "registry.opentofu.org/hashicorp/local" {
version = "2.4.1"
hashes = [
"h1:gndflVS5/+cwQbVVI6NwwB3dYK7W2QkmZuWmFBH/0tI=",
"h1:ZmiQvqAmblLC08P/NR+AVQtZrD+GGRpjFWcl+qX3+tE=",
"zh:0d4d7a980ba3479459ab8a231019588d38615b22b97600419c3c7482ee82bc8b",
"zh:16cf003a5bff1c5cae32e29af30f54376aec29b21ce26768d5fa59cc8b3bac3e",
"zh:469539918519547b2b87a9301ad811e59e50d4f217c36da01e0d981b45a393e9",
"zh:620234f4c7dcfd00fa4b7d15e71fa055ada4a8afe084f84a40d06d02fc7e7c08",
"zh:68fd2f982c5fecff972e03eced690077911083f782a237d5a026597ab387f737",
"zh:7bc30de9089a3d060f9866e08f6bd766351880026a458a4b7443cb110d47dc16",
"zh:ad772631dfa005fdd6f7cc2ce33d1771218574b5153c5d601d7584fe9e8a0da2",
"zh:cb6f16481607a3899e1d0a8f9fcacf246fb4927ee274da69c7d1e75fc4960713",
"zh:dcac954e9e401c53db3750ab3df22173f7ba288e978da348f97ddbbc4a33abb1",
"zh:eeeb0583e152403bdb473de5f133b48cef04af8f7c3f38ee7dc6d034df91534d",
]
}

View File

@ -1,9 +1,10 @@
# terraform
Contains Terraform code used to manage our infrastructure.
Contains [Terraform](https://terraform.io/) code used to manage our infrastructure, Nix'ified for [Terranix](https://terranix.org/).
## Prerequisites
- [Nix](https://nix.dev/) with [Flakes](https://nixos.wiki/wiki/Flakes) enabled
### Pre-commit hook
We want all our Terraform code to be well formatted and adhering to standards,
@ -25,13 +26,22 @@ a chance you push something that doesn't match our standards.
As we're using GitLab's Terraform image for our CI/CD pipeline, we'll stick to
using the latest version of Terraform. Instruction on how to install Terraform
can be found here: <https://www.terraform.io/downloads>
- [Terraform Cloud](https://app.terraform.io/) to use shared state
### Usage
- Run `nix develop -c $SHELL` to enter the development environment if not using [`direnv`](https://zero-to-flakes.com/direnv).
- Run `tofu login app.terraform.io` to log in to the Terraform Cloud backend
- Run `nix run` to apply changes.
- Run `nix flake update` to update dependencies.
### Authentication
Create a file `terraform.tfvars` containing:
```tfvars
```
do_token = "<GREENHOST_API_KEY>"
```
... substituting in our actual key.
### Managed state
@ -41,6 +51,8 @@ do_token = "<GREENHOST_API_KEY>"
- substitute in a personal access token in the shown command
- run the command locally to access the shared state
## [HCL to Nix](https://gist.github.com/KiaraGrouwstra/249ede6a7dfc00ea44d85bc6bdbcd875)
## Code-styling
We try to adhere to the

16
config.nix Normal file
View File

@ -0,0 +1,16 @@
{ config, lib, pkgs, options, specialArgs, ... }:
let
var = options.variable;
in rec {
resource = {
local_file.test_import = {
filename = "test_import.txt";
content = "A terranix created test file using imports. YEY!";
};
};
}

161
flake.lock Normal file
View File

@ -0,0 +1,161 @@
{
"nodes": {
"bats-assert": {
"flake": false,
"locked": {
"lastModified": 1636059754,
"narHash": "sha256-ewME0l27ZqfmAwJO4h5biTALc9bDLv7Bl3ftBzBuZwk=",
"owner": "bats-core",
"repo": "bats-assert",
"rev": "34551b1d7f8c7b677c1a66fc0ac140d6223409e5",
"type": "github"
},
"original": {
"owner": "bats-core",
"repo": "bats-assert",
"type": "github"
}
},
"bats-support": {
"flake": false,
"locked": {
"lastModified": 1548869839,
"narHash": "sha256-Gr4ntadr42F2Ks8Pte2D4wNDbijhujuoJi4OPZnTAZU=",
"owner": "bats-core",
"repo": "bats-support",
"rev": "d140a65044b2d6810381935ae7f0c94c7023c8c3",
"type": "github"
},
"original": {
"owner": "bats-core",
"repo": "bats-support",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1701680307,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1704999660,
"narHash": "sha256-nKC1rq0+9uswJzkVyBH6aTzKrE31GPNZHmLWfGhvXL8=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "6cd98a7a7575e7ff7923b1c84259da0b43ba827e",
"type": "github"
},
"original": {
"owner": "nixos",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"terranix": "terranix",
"terranix-hcloud": "terranix-hcloud"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"terranix": {
"inputs": {
"bats-assert": "bats-assert",
"bats-support": "bats-support",
"flake-utils": [
"flake-utils"
],
"nixpkgs": [
"nixpkgs"
],
"terranix-examples": "terranix-examples"
},
"locked": {
"lastModified": 1695406838,
"narHash": "sha256-xiUfVD6rtsVWFotVtUW3Q1nQh4obKzgvpN1wqZuGXvM=",
"owner": "terranix",
"repo": "terranix",
"rev": "fc9077ca02ab5681935dbf0ecd725c4d889b9275",
"type": "github"
},
"original": {
"owner": "terranix",
"repo": "terranix",
"type": "github"
}
},
"terranix-examples": {
"locked": {
"lastModified": 1636300201,
"narHash": "sha256-0n1je1WpiR6XfCsvi8ZK7GrpEnMl+DpwhWaO1949Vbc=",
"owner": "terranix",
"repo": "terranix-examples",
"rev": "a934aa1cf88f6bd6c6ddb4c77b77ec6e1660bd5e",
"type": "github"
},
"original": {
"owner": "terranix",
"repo": "terranix-examples",
"type": "github"
}
},
"terranix-hcloud": {
"inputs": {
"flake-utils": [
"flake-utils"
],
"nixpkgs": [
"nixpkgs"
],
"terranix": [
"terranix"
]
},
"locked": {
"lastModified": 1687791412,
"narHash": "sha256-5acZ+oKhoHmrlX1hRad6IisXyggR5prbCvMUuU7zlr0=",
"owner": "terranix",
"repo": "terranix-hcloud",
"rev": "6bcdfc395aeb37f83287f817eb620553534c3ff2",
"type": "github"
},
"original": {
"owner": "terranix",
"repo": "terranix-hcloud",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

102
flake.nix Normal file
View File

@ -0,0 +1,102 @@
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs";
flake-utils.url = "github:numtide/flake-utils";
terranix = {
url = "github:terranix/terranix";
inputs.nixpkgs.follows = "nixpkgs";
inputs.flake-utils.follows = "flake-utils";
};
terranix-hcloud = {
url = "github:terranix/terranix-hcloud";
inputs.nixpkgs.follows = "nixpkgs";
inputs.flake-utils.follows = "flake-utils";
inputs.terranix.follows = "terranix";
};
};
outputs = { self, nixpkgs, ... }@inputs:
inputs.flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
terraformConfiguration = inputs.terranix.lib.terranixConfiguration {
inherit system;
modules = [
inputs.terranix-hcloud.terranixModules.hcloud
./config.nix
];
};
tf = "${pkgs.opentofu}/bin/tofu";
in
{
defaultPackage = terraformConfiguration;
# Auto formatters. This also adds a flake check to ensure that the
# source tree was auto formatted.
treefmt.config = {
projectRootFile = "flake.nix";
programs = {
nixpkgs-fmt.enable = true;
};
};
# nix develop
devShell = pkgs.mkShell {
buildInputs = with pkgs; [
treefmt
inputs.terranix.defaultPackage.${system}
(opentofu.withPlugins (p: with p; [
hcloud # https://registry.terraform.io/providers/hetznercloud/hcloud/latest/docs
]))
];
};
# nix run ".#compile"
apps.compile = {
type = "app";
program = toString (pkgs.writers.writeBash "compile" ''
cp ${terraformConfiguration} config.tf.json
'');
};
# nix run ".#check"
apps.check = {
type = "app";
program = toString (pkgs.writers.writeBash "check" ''
if [[ -e config.tf.json ]]; then rm -f config.tf.json; fi
cp ${terraformConfiguration} config.tf.json \
&& ${tf} init \
&& ${tf} validate
'');
};
# nix run ".#apply"
apps.apply = {
type = "app";
program = toString (pkgs.writers.writeBash "apply" ''
if [[ -e config.tf.json ]]; then rm -f config.tf.json; fi
cp ${terraformConfiguration} config.tf.json \
&& ${tf} init \
&& ${tf} apply
'');
};
# nix run ".#destroy"
apps.destroy = {
type = "app";
program = toString (pkgs.writers.writeBash "destroy" ''
if [[ -e config.tf.json ]]; then rm -f config.tf.json; fi
cp ${terraformConfiguration} config.tf.json \
&& ${tf} init \
&& ${tf} destroy
rm ${toString ./.}/config.tf.json
rm ${toString ./.}/terraform.tfstate*
'');
};
# nix run
defaultApp = self.apps.${system}.apply;
formatter = pkgs.treefmt;
});
}

View File

@ -1,3 +0,0 @@
#!/usr/bin/env bash
cp precommit.sh .git/hooks/pre-commit

View File

@ -1,4 +0,0 @@
#!/usr/bin/env bash
set -e
terraform fmt -recursive -check -diff

179
main.tf
View File

@ -1,179 +0,0 @@
terraform {
backend "local" {}
# cloud {
# hostname = "app.terraform.io" # Optional; defaults to app.terraform.io
# organization = "bij1"
# workspaces {
# name = "infra"
# }
# }
required_providers {
digitalocean = {
# https://registry.terraform.io/providers/digitalocean/digitalocean/
source = "digitalocean/digitalocean"
# source = "bij1/greenhost"
version = "~> 2.21.0"
}
}
}
# Set the variable value in *.tfvars file
# or using -var="do_token=..." CLI option
variable "do_token" {}
# Configure the DigitalOcean Provider
provider "digitalocean" {
api_endpoint = "https://service.greenhost.net/api/"
token = var.do_token
}
###### REGIONS
data "digitalocean_regions" "available" {
# filter {
# key = "available"
# values = ["true"]
# }
# filter {
# key = "features"
# values = ["private_networking"]
# }
sort {
key = "name"
direction = "desc"
}
}
###### IMAGES
data "digitalocean_images" "available" {
# filter {
# key = "distribution"
# values = ["Ubuntu"]
# }
# filter {
# key = "regions"
# values = ["nyc3"]
# }
sort {
key = "created"
direction = "desc"
}
}
###### SSH KEYS
data "digitalocean_ssh_keys" "keys" {
# filter {
# key = "name"
# values = ["laptop", "desktop"]
# }
sort {
key = "name"
direction = "asc"
}
}
data "digitalocean_ssh_key" "kiara" {
name = "kiara"
}
# # Create a new SSH key
# resource "digitalocean_ssh_key" "key_kiara" {
# name = "kiara's key"
# public_key = file("/home/kiara/.ssh/id_rsa.pub")
# }
###### DROPLETS
# data "digitalocean_droplets" "droplets" {
# # filter {
# # key = "size"
# # values = ["s-1vcpu-1gb"]
# # }
# # filter {
# # key = "backups"
# # values = ["true"]
# # }
# sort {
# key = "created_at"
# direction = "desc"
# }
# }
# data "digitalocean_droplet" "cloud" {
# id = "8864"
# # name = "cloud.bij1.org"
# }
# # Create a new Droplet using the SSH key
# resource "digitalocean_droplet" "web" {
# image = "ubuntu-18-04-x64"
# name = "web-1"
# region = "nyc3"
# size = "s-1vcpu-1gb"
# ssh_keys = [digitalocean_ssh_key.default.fingerprint]
# }
# # Create a new Web Droplet in the nyc2 region
# resource "digitalocean_droplet" "web" {
# image = "ubuntu-18-04-x64"
# name = "web-1"
# region = "nyc2"
# size = "s-1vcpu-1gb"
# backups = false
# monitoring = false
# ipv6 = false
# # vpc_uuid = ""
# # ssh_keys = [] # change = recreate!
# # resize_disk = true
# # tags = []
# # user_data = ""
# # volume_ids = []
# # droplet_agent = bool
# # graceful_shutdown = false
# }
###### DNS RECORDS
data "digitalocean_records" "records" { # records_bij1_net
domain = "bij1.net"
# filter {
# key = "type"
# values = ["MX"]
# }
sort {
key = "name"
direction = "asc"
}
}
# data "digitalocean_records" "records_bij1_org" {
# domain = "bij1.org"
# # filter {
# # key = "type"
# # values = ["MX"]
# # }
# sort {
# key = "name"
# direction = "asc"
# }
# }
data "digitalocean_record" "static" {
domain = "bij1.net"
name = "static"
}
# # Add an A record to the domain.
# resource "digitalocean_record" "foo" {
# domain = "bij1.org"
# type = "A"
# name = "foo"
# value = "192.168.0.11"
# ttl = 86400
# }

View File

@ -1,35 +0,0 @@
output "regions_output" {
value = data.digitalocean_regions.available.regions
}
output "images_output" {
value = data.digitalocean_images.available
}
output "keys_output" {
value = data.digitalocean_ssh_keys.keys
}
output "key_output" {
value = data.digitalocean_ssh_key.kiara.public_key
}
# output "droplets_output" {
# value = data.digitalocean_droplets.droplets
# }
# output "droplet_output" {
# value = data.digitalocean_droplet.example.ipv4_address
# }
output "records_output" {
value = data.digitalocean_records.records
}
output "record_type" {
value = data.digitalocean_record.static.type
}
# output "fqdn" {
# value = digitalocean_record.foo.fqdn
# }

View File

@ -1,11 +0,0 @@
#!/usr/bin/env bash
echo "Checking if pre-commit hook is up to date"
diff precommit.sh .git/hooks/pre-commit
if [[ $? != 0 ]]
then
echo "Pre-commit hook is not up to date, please run ./install_precommit_hook.sh and commit again!"
exit 1
fi
echo "${GIT_DIR}"
./lint.sh

5
treefmt.toml Normal file
View File

@ -0,0 +1,5 @@
# One CLI to format the code tree - https://github.com/numtide/treefmt
[formatter.nix]
command = "nixpkgs-fmt"
includes = [ "*.nix"]

View File