This Guide setups terraform and a CICD pipeline to Control DNS records using Gitlab remote state backend and Gitops
Requirements #
- Terraform CLI
- Gitlab Runner
- 2 Instances of pihole
- Runner can talk to Piholes on network
Setup Main #
Lets configure the main.tf file and add the required providers. We will be using Gitlab for a state backend
main.tf
terraform {
required_providers {
pihole = {
source = "ryanwholey/pihole"
}
kubectl = {
source = "gavinbunney/kubectl"
version = ">= 1.7.0"
}
}
backend "http" {
}
}
Locals #
The locals.tf file needs to be configured with the following
Change to you pihole ips and correct email and the domain ip is the one you gave traefik. mine is 10.10.1.60
locals {
pihole_primary_url = "http://x.x.x.x:8888" # IF you have it on a different port
pihole_secondary_url = "http://x.x.x.x" # If you didnt change the port setting
domain_name = "domain.name"
domain_ip = "x.x.x.x"
}
| Field | Description |
|---|---|
| pihole_primary_url | Url to the admin console of your piholes |
| domain_name | The Domain Name to be used for creating the A record |
| domain_ip | The target the A record will point to, example our traefik reverse proxy load balancer ip |
Variables #
add this to variables.tf
variable "PIHOLE_PASSWORD" {
type = string
sensitive = true
}
Providers #
Create a providers.tf
provider "pihole" {
url = local.pihole_primary_url # PIHOLE_URL
password = var.PIHOLE_PASSWORD # PIHOLE_PASSWORD
}
provider "pihole" {
url = local.pihole_secondary_url # PIHOLE_URL
password = var.PIHOLE_PASSWORD # PIHOLE_PASSWORD
alias = "secondary"
}
Modules #
Now lets create a modules reference for our code to run
dns.tf
module "pihole_1_io" {
source = "./modules/pihole"
domain_name = local.domain_name
domain_ip = local.domain_ip
providers = {
pihole = pihole
}
}
module "pihole_2_io" {
source = "./modules/pihole"
domain_name = local.domain_name
domain_ip = local.domain_ip
providers = {
pihole = pihole.secondary
}
}
Pihole Module #
Create Folders modules/pihole/
create files
- a-records.tf
- cname.tf
- main.tf
- outputs.tf
- variables.tf
a-recoords.tf #
point a record to our load balancer
resource "pihole_dns_record" "mantisd-io" {
domain = var.domain_name
ip = var.domain_ip
}
cname.tf #
Create records to both our piholes admin dashboard
resource "pihole_cname_record" "pihole" {
domain = "pihole.${var.domain_name}"
target = "${var.domain_name}"
}
resource "pihole_cname_record" "pihole-2" {
domain = "pihole-2.${var.domain_name}"
target = "${var.domain_name}"
}
main.tf #
terraform {
required_version = ">=1.1.4"
required_providers {
pihole = {
source = "ryanwholey/pihole"
}
}
}
data "pihole_cname_records" "cname_records" {
}
data "pihole_dns_records" "dns_records" {
}
outputs.tf #
output "dns_records" {
value = data.pihole_dns_records.dns_records
}
output "cname_record" {
value = data.pihole_cname_records.cname_records
}
variables.tf #
variable "domain_name" {
type = string
}
variable "domain_ip" {
type = string
}
Add new Records to cname.tf and a-records.tf
CICD #
Now lets create a pipeline to automatically deploy new routes in the main branch when they are commited to our git repo
gitlab-ci.yml #
stages:
- pre
- build
- deploy
default:
tags:
- your
- runner
- tags
variables:
TF_ROOT: $CI_PROJECT_DIR # The relative path to the root directory of the Terraform project
TF_STATE_NAME: pihole-records # The name of the state file used by the GitLab Managed Terraform state backend
TF_IMAGE: registry.gitlab.com/gitlab-org/terraform-images/releases/terraform:1.1.9
ARTIFACT_VERSION: "1.0.0"
REALEASE_VERSION: $ARTIFACT_VERSION
TF_ADDRESS: "https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/terraform/state/${TF_STATE_NAME}"
tf_init_var_check:
stage: .pre
script:
- if [ -z "${TF_ADDRESS}"]; then echo "ERROR - TF_ADDRESS does not exist, add to .gitlab-ci.yml"; exit 1; fi
- if [ -z "${TF_USERNAME}"]; then echo "ERROR - TF_USERNAME does not exist, add to .gitlab-ci.yml"; exit 1; fi
- if [ -z "${TF_PASSWORD}"]; then echo "ERROR - TF_PASSWORD does not exist, add to .gitlab-ci.yml"; exit 1; fi
.terraform_init:
script:
- echo $GITLAB_USER_NAME
- rm -rf .terraform
- terraform --version
- |
terraform init \
-backend-config=address=${TF_ADDRESS} \
-backend-config=lock_address=${TF_ADDRESS}/lock \
-backend-config=unlock_address=${TF_ADDRESS}/lock \
-backend-config=username=${TF_USERNAME} \
-backend-config=password=${TF_PASSWORD} \
-backend-config=lock_method=POST \
-backend-config=unlock_method=DELETE \
-backend-config=retry_wait_min=5
tf_base_config_var_check:
stage: .pre
script:
- if [ -z "${TF_IMAGE}"]; then echo "ERROR - TF_IMAGE does not exist, add to .gitlab-ci.yml"; exit 1; fi
- if [ -z "${TF_ROOT}"]; then echo "ERROR - TF_ROOT does not exist, add to .gitlab-ci.yml"; exit 1; fi
.tf_base_config:
image: ${TF_IMAGE}
cache:
key: "${TF_ROOT}"
paths:
- ${TF_ROOT}/.terraform/
plan:
stage: build
extends:
- .tf_base_config
script:
- !reference [.terraform_init, script]
- terraform plan -out "plans"
artifacts:
paths:
- plans
expire_in: 20m
deploy:
stage: deploy
extends:
- .tf_base_config
script:
- !reference [.terraform_init, script]
- terraform apply -input=false "plans"
only:
- master
Now When changes are added to master branch the pipeline will execute and deploy to your piholes