Hallo! und willkommen!! In diesem Projekt werden wir eine einfache Infrastruktur mit Terraform erstellen und dabei den Terraform-Kern-Workflow (Schreiben, Planen & Anwenden) kollaborativ für den Produktionseinsatz implementieren. Einfach ausgedrückt werden wir Infrastruktur mit Terraform so bereitstellen, dass ein Team von Ingenieuren Änderungen am System einführen, diese detailliert überprüfen, genehmigen oder ablehnen und (falls genehmigt) in der Produktion bereitstellen kann.
Das System, das wir in diesem Projekt aufbauen, eignet sich für den Betrieb kleiner bis mittelständischer Organisationen (mit oder ohne die mögliche Ergänzung einiger weiterer Dinge, über die wir am Ende des Projekts sprechen werden).
Für dieses Projekt sind Sie ein Infrastructure Engineer, der mit der Erstellung der initialen Infrastruktur für ein mittelständisches Unternehmen beauftragt ist. Wir beginnen damit, unsere Infrastruktur von Grund auf neu zu erstellen, arbeiten durchgehend mit Versionskontrolle (Git), bootstrappen unser System über ein lokales Backend auf unserer Workstation, migrieren es auf ein Remote-Backend, um Zusammenarbeit zu ermöglichen, und erstellen Skripte, um den gesamten Prozess der Continuous Integration und Continuous Deployment zu automatisieren.
Wir werden:
- Eine grundlegende Infrastruktur mit Terraform auf DigitalOcean & Cloudflare erstellen
- Ein Google Cloud Storage Remote-Backend konfigurieren, um Zusammenarbeit zu ermöglichen
- Den Zugriff auf unser GCS-Backend über Github Environments & Actions-Workflows konfigurieren
- Actions-Workflow-Skripte erstellen, um unsere Infrastrukturänderungen zu initialisieren, zu validieren, in der Vorschau anzuzeigen und anzuwenden.
- Github Pull Requests und den Github Bot für die Überprüfung (sowie Genehmigung oder Ablehnung) der vorgeschlagenen Infrastrukturänderungen nutzen.
Hier ist das Projekt-Repository auf Github: https://github.com/obiMadu/terraform-github-workflow
Lassen Sie uns direkt eintauchen 🚀
1. Erstellen unserer Infrastruktur über Terraform

Wir werden die sehr einfache Infrastruktur erstellen, die im obigen Diagramm dargestellt ist. Ein einfacher Webserver, auf dem Nginx auf Digital Ocean läuft, mit einer IPv4-Adresse und einem DNS A record in einer Cloudflare-Zone, der eine Subdomain darauf verweist. Wir werden dies erreichen, indem wir nur zwei Terraform-Ressourcen erstellen.
Sie könnten Cloudflare leicht gegen jeden anderen Domain Name Service Ihrer Wahl austauschen. Wenn Sie Ihre Domains beispielsweise über Amazon Route 53 verwalten, müssen Sie für dieses Tutorial nicht zu Cloudflare wechseln. Erstellen Sie einfach die entsprechenden Terraform-Konfigurationen, die erforderlich sind, um die IPv4-Adresse von unserer Serverinstanz auf einen Domainnamen abzubilden (es muss auch keine Subdomain sein, Sie können eine Apex-Domain abbilden, wie auch immer Sie es bevorzugen).
Wie (wahrscheinlich) erwartet, folgt daraus auch, dass Sie Ihren Server nicht auf Digital Ocean erstellen müssen. Sie könnten ihn auf AWS oder bei jedem anderen Anbieter Ihrer Wahl bereitstellen, vorausgesetzt, wir erhalten einen Server mit installiertem Nginx und einer IPv4-Adresse.
Um die Sache noch interessanter zu machen, könnten Sie sogar einen Basis-Server bereitstellen und ein Tool wie Ansible verwenden, um Nginx oder eine andere Web-App darauf bereitzustellen. Sie könnten Ihr eigenes benutzerdefiniertes Image mit Tools wie Packer erstellen und dieses ebenfalls verwenden. Ihre Entscheidung. Dieses Projekt zielt darauf ab, schnell und einfach nachvollziehbar zu sein.
1.1 Erstellen von providers.tf
Als Nächstes werden wir eine providers.tf-Datei erstellen, um unsere verschiedenen Provider zu konfigurieren. Die Versionsbeschränkungen der Provider sind auf die neuesten zum Zeitpunkt der Erstellung dieses Artikels festgelegt.
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "2.34.1"
}
cloudflare = {
source = "cloudflare/cloudflare"
version = "4.24.0"
}
}
}
# Digital Ocean API credentials
provider "digitalocean" {
token = var.do_token
}
provider "cloudflare" {
api_token = var.cloudflare_api_token
}Das Obige sind die absoluten Mindestargumente, die von den verschiedenen Providern benötigt werden, um ordnungsgemäß zu funktionieren. Mit dieser erstellten Datei werden wir Git in unserem Projektverzeichnis initialisieren und diese Datei zu unserem allerersten Commit über die folgenden Befehle machen;
git init .
git add .
git commit -m "Initial commit"Wir haben unsere Argumentwerte in Variablen umgewandelt, um die harte Codierung dieser sensiblen Werte in unseren Konfigurationsdateien zu verhindern. Denken Sie auch daran, dass Sie die Git-Commit-Nachrichten nach Belieben anpassen können.
1.2 Erstellen von variables.tf
Da wir unseren Provider-Konfigurationen Variablen als Werte für die Argumente zugewiesen haben, ist es an der Zeit, diese Variablen zu deklarieren. Wir tun dies in einer Datei, die wir variables.tf nennen, mit folgendem Inhalt.
variable "do_token" {
type = string
}
variable "do_region" {
type = string
}
variable "cloudflare_api_token" {
type = string
}
variable cloudflare_zone_id" {
type = string
}Wir können nun fortfahren und diese neue Datei mit einer ordnungsgemäßen Commit-Nachricht an Git übergeben.
1.3 Initialisieren von Terraform
Nun, da wir unsere Basiskonfiguration haben, ist es an der Zeit, terraform init in unserem Projektverzeichnis auszuführen, um Terraform zu initialisieren.

Ihre Erfolgsmeldung sollte ähnlich wie oben aussehen.
Nun müssen wir noch zwei weitere Dinge tun;
- Dem Projekt eine für Terraform geeignete
.gitignore-Datei hinzufügen - Die Werte für die verschiedenen Variablen bereitstellen, für deren Verwendung wir Terraform konfiguriert haben.
Wir können eine für Terraform geeignete .gitignore-Datei von der folgenden Adresse abrufen https://github.com/github/gitignore/blob/main/Terraform.gitignoretext
Nun ist es an der Zeit, die verschiedenen API-Schlüssel, die für unsere Provider erforderlich sind, von ihren jeweiligen Plattformen abzurufen. Wir benötigen;
- Ein API-Token mit
write-Zugriff von Digital Ocean, zusammen mit einem Regions-Shortcode (der angibt, in welcher Region wir unsere Ressourcen bereitstellen möchten) - Ein API-Token von Cloudflare, zusammen mit der Zonen-ID für die Domain, innerhalb derer wir unseren DNS-Eintrag erstellen möchten.
Wie genau Sie diese Anmeldeinformationen erhalten, wird hier nicht behandelt (Sie sollten in der Lage sein, sie zu erwerben).
Der Einfachheit halber können Sie die DigitalOcean-Region fra1 verwenden. Diese entspricht dem Rechenzentrum in Frankfurt.
Nun ist es an der Zeit, Terraform die von uns erworbenen Werte zur Verfügung zu stellen. Um unkompliziert zu sein, werden wir eine terraform.tfvars-Datei erstellen und unsere Variablenwerte einspeisen. Unsere Datei sollte in etwa wie folgt aussehen;
do_token = "value"
do_region = "fra1"
cloudflare_api_token = "value"
cloudflare_zone_id = "value"DENKEN SIE DARAN: Sie dürfen die Datei terraform.tfvars niemals an Git übergeben. Diese Datei enthält wichtige Anwendungsgeheimnisse, die mit niemandem geteilt werden dürfen. Wenn ein Bedrohungsakteur Zugriff auf diese Geheimnisse erhält, ist unsere gesamte Infrastruktur in Gefahr, kompromittiert zu werden. Der einzige Zweck dieser Datei ist die lokale Verwendung. Wir werden eine sicherere Methode anwenden, um diese Werte für Terraform bereitzustellen, wenn wir in die CI/CD-Umgebung gelangen.
1.4 Erstellen der Ressourcen
Nun ist es an der Zeit, die Ressourcen für unsere Infrastruktur tatsächlich zu erstellen. Wir werden zwei Dateien erstellen: eine servers.tf-Datei und eine dns.tf-Datei.
- In die erste Datei,
servers.tf, fügen wir Ressourcenkonfigurationen ein, um unseren DigitalOcean-Server mit dem offiziellen DigitalOcean Nginx-Image zu erstellen. Und zwar so (wir erstellen eine Ressourcedigitalocean_droplet);
#create server with nginx image
resource "digitalocean_droplet" "server" {
name = "gate"
size = "s-1vcpu-1gb"
image = "nginx"
region = var.do_region
}- Die zweite Datei,
dns.tf, konfigurieren wir wie folgt (wir erstellen eine Ressourcecloudflare_record);
# Add a record to the domain
resource "cloudflare_record" "server" {
zone_id = var.cloudflare_zone_id
name = "server"
value = digitalocean_droplet.server.ipv4_address
type = "A"
ttl = 1
proxied = true
}Speichern Sie nun beide Dateien und übergeben Sie sie mit ordnungsgemäßen Commit-Nachrichten an Git.
Zu diesem Zeitpunkt sollte Ihr Verzeichnisbaum genau so aussehen;

2. Erstellen und Initialisieren des Remote-Backends
Es ist an der Zeit, unserem Projekt ein Remote-Backend hinzuzufügen. Da ich ein Google Cloud Engineer bin, werde ich in diesem Projekt mit einem gcs Remote-Backend arbeiten. Sie können dies in jedes andere Remote-Backend Ihrer Wahl ändern, beispielsweise in ein s3-Backend.
Also habe ich einen Google Cloud Storage Bucket mit dem Namen terraform-github-workflow erstellt. Ich habe ein Dienstkonto unter demselben Namen erstellt und dem Prinzipal die Berechtigung Storage Object Admin für den GCS-Bucket zugewiesen. Dadurch kann das Dienstkonto Objekte im Bucket terraform-github-workflow erstellen und verwalten. Anschließend habe ich einen JSON service account key für den Dienstkonto-Prinzipal erstellt, den ich heruntergeladen und an einem guten Ort auf meiner lokalen Workstation gespeichert habe.
Der letzte Schritt bestand darin, die Umgebungsvariable GOOGLE_APPLICATION_CREDENTIALS auf den absoluten Pfad des JSON-Schlüssels zu setzen, der auf meinen PC heruntergeladen wurde, und zwar so;
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/json/key/fileUm sicherzugehen, können Sie jederzeit einen anderen service account key erstellen, den Sie verwenden, wenn wir in die CI/CD-Umgebung gelangen.
Schließlich ist es an der Zeit, terraform init auszuführen, um dieses neue Backend zu initialisieren. Abhängig vom gewählten Backend sollten Sie eine Erfolgsmeldung sehen, die in etwa so aussieht;

3. Erstellen von Github Actions-Workflows
Nun, da unser Terraform Remote-Backend initialisiert ist, ist es an der Zeit, die Github Actions-Skripte für unsere CI/CD-Pipeline zu erstellen. Wir werden insgesamt 3 verschiedene Workflows wie folgt erstellen;
- Ein
validate-Workflow, der bei jedem Push in denplan-Branch unseres Projekts ausgelöst wird. Dieser wird alle neuen Änderungen an unserer Codebasis mithilfe des Befehlsterraform validatevalidieren. - Ein
plan-Workflow, der bei jedem Pull Request in denmain-Branch des Projekts ausgelöst wird. Dieser Workflow führt einen Trockenlauf der anstehenden Infrastrukturänderungen durch und verwendet den Github Bot, um diese Änderungen als Kommentar zu diesem Pull Request hinzuzufügen. - Ein
deploy-Workflow, der bei jedem Push/Merge in denmain-Branch ausgelöst wird. Dieser stellt unsere genehmigten Infrastrukturänderungen bereit.
3.1 Erstellen von workflows/validate.yml
Wir beginnen mit der Erstellung eines .github/workflows-Ordners im Stammverzeichnis unseres Projekts. Innerhalb des workflows-Verzeichnisses erstellen wir eine validate.yml-Datei. Unser Verzeichnisbaum sollte zu diesem Zeitpunkt so aussehen;

Hier ist der Inhalt der validate-Workflow-Datei;
name: Validate
on:
push:
branches: [plan]
env:
GOOGLE_APPLICATION_CREDENTIALS: ${{ vars.GOOGLE_APPLICATION_CREDENTIALS }}
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true # Fetch submodules (true OR recursive)
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_wrapper: true
- name: Setup Backend Credentials
id: backend
run: echo '${{ secrets.GCS_KEY }}' > ${{ vars.GOOGLE_APPLICATION_CREDENTIALS }}
- name: Terraform Init
id: init
run: terraform init
- name: Terraform Validate
id: terraform
run: terraform validate -no-colorDieser Workflow:
- Wird bei jedem Push in den
plan-Branch ausgelöst - Läuft auf dem
latest Ubuntu-Image - Checkt unseren Code als ersten Schritt aus
- Richtet dann Terraform ein
- Richtet die
gcs-Backend-Anmeldeinformationen ein - Führt
terraform initaus & - Führt
terraform validateauf unserer Codebasis aus
Lassen Sie uns nun diese Datei an Git übergeben und fortfahren.
3.2 Erstellen von workflows/deploy.yml
Als Nächstes erstellen wir unseren deploy-Workflow mit folgendem Inhalt;
name: Deploy
on:
push:
branches: [main]
env:
GOOGLE_APPLICATION_CREDENTIALS: ${{ vars.GOOGLE_APPLICATION_CREDENTIALS }}
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true # Fetch submodules (true OR recursive)
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
- name: Setup Backend Credentials
id: backend
run: echo '${{ secrets.GCS_KEY }}' > ${{ vars.GOOGLE_APPLICATION_CREDENTIALS }}
- name: Terraform Init
id: init
run: terraform init
- name: Terraform Apply
id: apply
env:
TF_VAR_do_token: ${{ secrets.do_token }}
TF_VAR_do_region: ${{ secrets.do_region }}
TF_VAR_cloudflare_api_token: ${{ secrets.cloudflare_api_token }}
TF_VAR_cloudflare_zone_id: ${{ secrets.cloudflare_zone_id }}
run: terraform apply -auto-approveDieser Workflow:
- Wird bei jedem Push/Merge in den
main-Branch ausgelöst - Läuft auf dem
latest Ubuntu-Image - Checkt unseren Code als ersten Schritt aus
- Richtet dann Terraform ein
- Richtet die
gcs-Backend-Anmeldeinformationen ein - Führt
terraform initaus & - Führt
terraform applymit-auto-approveaus, um unsere Infrastruktur bereitzustellen
Übergeben Sie diesen Workflow noch einmal mit einer ordnungsgemäßen Commit-Nachricht an Git, und wir erstellen unseren letzten Workflow.
3.3 Erstellen von workflows/plan.yml
Der letzte (aber definitiv nicht der unwichtigste) Workflow, den wir erstellen, ist der plan-Workflow. Wir erstellen diesen Workflow zuletzt, da wir darin unseren Github Bot integrieren werden, um die Ausgaben unserer Trockenläufe bereitzustellen, sodass unsere Pull-Request-Überprüfungen einfacher und wertvoller werden.
Wir werden also zuerst den Workflow mit den erforderlichen Funktionen erstellen und dann im nächsten Schritt den Bot integrieren. Unten ist der Code für unseren plan-Workflow;
name: Plan
on:
pull_request:
branches: [main]
env:
GOOGLE_APPLICATION_CREDENTIALS: ${{ vars.GOOGLE_APPLICATION_CREDENTIALS }}
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v2
with:
submodules: true # Fetch submodules (true OR recursive)
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_wrapper: true
- name: Setup Backend Credentials
id: backend
run: echo '${{ secrets.GCS_KEY }}' > ${{ vars.GOOGLE_APPLICATION_CREDENTIALS }}
- name: Terraform Init
id: init
run: terraform init
- name: Terraform fmt
id: fmt
run: terraform fmt -check
continue-on-error: true
- name: Terraform Validate
id: validate
run: terraform validate -no-color
- name: Terraform Plan
id: plan
env:
TF_VAR_do_token: ${{ secrets.do_token }}
TF_VAR_do_region: ${{ secrets.do_region }}
TF_VAR_cloudflare_api_token: ${{ secrets.cloudflare_api_token }}
TF_VAR_cloudflare_zone_id: ${{ secrets.cloudflare_zone_id }}
run: terraform plan -no-colorDieser Workflow:
- Wird bei jedem Pull Request in den
main-Branch ausgelöst - Läuft auf dem
latest Ubuntu-Image - Erwirbt die Berechtigung, in Pull Requests zu schreiben
- Checkt unseren Code als ersten Schritt aus
- Richtet dann Terraform ein
- Richtet die
gcs-Backend-Anmeldeinformationen ein - Führt
terraform initaus - Führt
terraform fmtaus - Führt
terraform validate(erneut) aus - Führt
terraform planfür einen Trockenlauf unserer Infrastruktur aus
Perfekt! Nun können wir fortfahren und den Bot im nächsten Abschnitt hinzufügen. Denken Sie daran, Ihren neuen plan-Workflow an Git zu übergeben.
4. Einrichten des Github Bots
Das Hinzufügen des Github Bots zu unserem Workflow ist ziemlich unkompliziert. Es ist nur ein zusätzlicher Schritt in unserem Plan-Workflow.
Fügen Sie Ihrer plan.yml-Workflow-Datei den folgenden Schritt hinzu:
- name: Comment plan on PR
uses: actions/github-script@v6
if: github.event_name == 'pull_request'
env:
PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
// 1. Retrieve existing bot comments for the PR
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
})
const botComment = comments.find(comment => {
return comment.user.type === 'Bot' && comment.body.includes('Terraform Format and Style')
})
// 2. Prepare format of the comment
const output = ` ## Terraform Results
#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
#### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
#### Terraform Validation 🤖\`${{ steps.validate.outcome }}\`
<details><summary>Validation Output</summary>
\`\`\`\n
${{ steps.validate.outputs.stdout }}
\`\`\`
</details>
#### Terraform Plan 📖\`${{ steps.plan.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\`\n
${process.env.PLAN}
\`\`\`
</details>
*Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Working Directory: \`${{ env.tf_actions_working_dir }}\`, Workflow: \`${{ github.workflow }}\`*`;
// 3. If we have a comment, update it, otherwise create a new one
if (botComment) {
github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: output
})
} else {
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
}Ihre gesamte plan.yml-Datei sollte nun genau so aussehen:
name: Plan
on:
pull_request:
branches: [main]
env:
GOOGLE_APPLICATION_CREDENTIALS: ${{ vars.GOOGLE_APPLICATION_CREDENTIALS }}
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v2
with:
submodules: true # Fetch submodules (true OR recursive)
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_wrapper: true
- name: Setup Backend Credentials
id: backend
run: echo '${{ secrets.GCS_KEY }}' > ${{ vars.GOOGLE_APPLICATION_CREDENTIALS }}
- name: Terraform Init
id: init
run: terraform init
- name: Terraform fmt
id: fmt
run: terraform fmt -check
continue-on-error: true
- name: Terraform Validate
id: validate
run: terraform validate -no-color
- name: Terraform Plan
id: plan
env:
TF_VAR_do_token: ${{ secrets.do_token }}
TF_VAR_do_region: ${{ secrets.do_region }}
TF_VAR_cloudflare_api_token: ${{ secrets.cloudflare_api_token }}
TF_VAR_cloudflare_zone_id: ${{ secrets.cloudflare_zone_id }}
run: terraform plan -no-color
- name: Comment plan on PR
uses: actions/github-script@v6
if: github.event_name == 'pull_request'
env:
PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
// 1. Retrieve existing bot comments for the PR
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
})
const botComment = comments.find(comment => {
return comment.user.type === 'Bot' && comment.body.includes('Terraform Format and Style')
})
// 2. Prepare format of the comment
const output = ` ## Terraform Results
#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
#### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
#### Terraform Validation 🤖\`${{ steps.validate.outcome }}\`
<details><summary>Validation Output</summary>
\`\`\`\n
${{ steps.validate.outputs.stdout }}
\`\`\`
</details>
#### Terraform Plan 📖\`${{ steps.plan.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\`\n
${process.env.PLAN}
\`\`\`
</details>
*Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Working Directory: \`${{ env.tf_actions_working_dir }}\`, Workflow: \`${{ github.workflow }}\`*`;
// 3. If we have a comment, update it, otherwise create a new one
if (botComment) {
github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: output
})
} else {
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
}5. Codebasis zu Github pushen
Nachdem Sie mit der Übergabe der oben genannten neuen Ergänzungen an Git fertig sind, ist es an der Zeit, dass wir unsere Infrastruktur-Codebasis auf Github publish.
Gehen Sie nun einfach zu Github und erstellen Sie ein öffentliches (oder privates) Repository, fügen Sie das Repository als origin-Remote für das Projekt auf unserer lokalen Maschine hinzu und pushen Sie es.
Sofort danach sollte Ihr deploy-Workflow beginnen auszuführen, aber keine Sorge, er wird fehlschlagen. Er wird fehlschlagen, da die Github Actions-Umgebung nicht mit den richtigen Anmeldeinformationen konfiguriert wurde, um sowohl unser Terraform-Backend zu initialisieren als auch die entsprechenden Werte für die in unserer Infrastruktur definierten Variablen bereitzustellen.


Nun ist es an der Zeit, die Github Actions CI/CD-Umgebung ordnungsgemäß mit den entsprechenden Zugangsdaten zu konfigurieren, um unseren Workload bereitzustellen.
6. Konfigurieren von Github Environments für das Terraform-Backend und die Variablen
Nun müssen wir zu Github Environments gehen und die secrets und env vars konfigurieren, die für den Erfolg unseres Workflows erforderlich sind.
Wenn Sie in unseren Github Actions-Workflows aufgepasst haben, haben wir für jeden Vorgang, der sich auf den Terraform-Status auswirkt, die verschiedenen Umgebungsvariablen eingefügt, die von Terraform zur Ausführung benötigt werden. Ein Beispiel hierfür findet sich beim Befehl terraform apply im deploy-Workflow. Der Code-Ausschnitt lautet wie folgt;
- name: Terraform Apply
id: apply
env:
TF_VAR_do_token: ${{ secrets.do_token }}
TF_VAR_do_region: ${{ secrets.do_region }}
TF_VAR_cloudflare_api_token: ${{ secrets.cloudflare_api_token }}
TF_VAR_cloudflare_zone_id: ${{ secrets.cloudflare_zone_id }}
run: terraform apply -auto-approveUnten ist also eine Liste aller verschiedenen secrets und env vars, die wir festlegen müssen (Sie werden sehen, dass sie alle an den entsprechenden Stellen in die verschiedenen Workflows integriert wurden);
- Terraform-Variablen
TF_VAR_do_tokenTF_VAR_do_regionTF_VAR_cloudflare_api_tokenTF_VAR_cloudflare_zone_id
- Remote-Backend-Secrets & Variablen
GCS_KEY- Der Google Cloud Storage Schlüssel.GOOGLE_APPLICATION_CREDENTIALS- Pfad zum Google Cloud Storage Schlüssel im Dateisystem
Denken Sie daran, einen anderen GCS Key für die Github Actions-Umgebung zu erstellen und den zu deaktivieren, den wir zur lokalen Initialisierung von Terraform verwendet haben. Von nun an werden alle neuen Änderungen an der Infrastruktur über unseren Workflow verarbeitet. Sie sollten Ihren Schlüssel nicht einmal für die anfängliche Bereitstellung von Ressourcen verwenden.
Gehen Sie nun zu Ihrer Seite Github Dashboard -> Settings -> Secrets and Variables -> Actions. Das ist die unten dargestellte Seite.

Als Nächstes werden wir die Werte für die verschiedenen Secrets und Umgebungsvariablen ausfüllen.
Da wir in diesem Projekt nur mit einer einzigen Umgebung arbeiten, werden wir diese als Repository-Secrets und -Variablen ausfüllen. In einem fortgeschritteneren Setup, bei dem wir Entwicklungs-, Staging-, QA- und Produktionsumgebungen hätten, würden wir die Kernlösung von Github Environments nutzen. Dieses Projekt geht nicht so weit in die Tiefe, daher bleiben wir bei den Repo-Secrets und -Variablen.
-
Abgesehen von der Variablen
GOOGLE_APPLICATION_CREDENTIALSsollte alles andere ein Geheimnis sein. Verwenden Sie für diese Variable den Wert./key.json, um es einfach zu halten. -
Um zwischen
secretsundvariableszu wechseln, nutzen Sie die Registerkarten unter den gleichen Namen auf der Seite.
Wenn alles eingestellt ist, sollten wir Seiten haben, die wie folgt aussehen:


Nun sind alle unsere Secrets und Variablen konfiguriert. 🎉 🎉
7. Testen des gesamten Workflows
Schließlich ist es an der Zeit, dieses riesige Rad in Bewegung zu setzen.
7.1 Erstellen des plan-Branches
Der erste Schritt wird sein, den plan-Branch des Projekts zu erstellen. Wir haben ihn in den vorherigen Schritten oft erwähnt, es ist an der Zeit, ihn tatsächlich zu erstellen.
Wir werden das mit dem folgenden Befehl erreichen:
git checkout -b planDadurch wird der neue plan-Branch erstellt und wir in unserem aktuellen Arbeitsverzeichnis dorthin gewechselt (ausgecheckt). Als Nächstes pushen wir diesen Branch zu Github und setzen ihn so, dass er einen neuen Origin-Branch unter dem gleichen plan-Namen verfolgt.
git push -u origin planDieser Push sollte einen validate-Workflow auf Github auslösen. Wenn dieser erfolgreich ist, sind wir auf dem richtigen Weg. Wenn nicht, müssen Sie sich umsehen, um herauszufinden, was Sie möglicherweise übersehen haben, um es zu beheben. Sehen Sie sich die Fehlerprotokolle des Actions-Workflows an, um Fehler zu verstehen.
Wenn Sie dieser Anleitung jedoch religiös gefolgt sind, sollten Sie Erfolgsbildschirme ähnlich den unten stehenden haben. Ihr allererster validate-Workflow sollte ein Erfolg sein.


7.2 Vorbereiten auf und Erstellen des allerersten PR zu main
Um unseren Workflow in Gang zu bringen, müssen wir unseren ersten Pull Request an den main-Branch erstellen. Zu diesem Zeitpunkt wird das unmöglich sein, da unsere main- und plan-Branches synchronisiert sind. Um es zum Laufen zu bringen, müssen wir einen Dummy-Commit in den plan-Branch machen.
Um es einfach zu halten, fügen wir einfach einen Kommentar zu einer unserer Infrastrukturdateien hinzu, übergeben die Änderung und erstellen einen PR zu main.
Sie können einer beliebigen Datei Ihrer Wahl einen Kommentar hinzufügen. Ich werde das bei der Datei backend.tf tun. Ich werde die folgenden Zeilen am Anfang der Datei hinzufügen
# Configure the remote gcs backendSpeichern Sie nun Ihre geänderte Datei, übergeben (commit) Sie sie und pushen Sie sie zu Github. Das sollte einen neuen validate-Workflow-Durchlauf auf Github starten;

Und es sollte erfolgreich sein.

Nun können wir fortfahren und unseren ersten Pull Request zu main erstellen. Wenn alles gut funktioniert, sollten Sie Ausgaben ähnlich den unten abgebildeten erhalten.


In Ihrem Pull Request sollte jetzt alles grün sein 🎉 🎉
7.3 Untersuchen der Plan-Ausgaben über den Bot-Kommentar
Nachdem Ihre Workflows erfolgreich ausgeführt wurden, sollten Sie den neuen Bot-Kommentar sehen können, der sehr wichtige Details zu Ihren vorgeschlagenen Infrastrukturänderungen enthält.

Gehen Sie voran und erweitern Sie die verschiedenen Abschnitte des Kommentars, um mehr Details zu erhalten. Ein Klick auf die Schaltfläche Show Plan zeigt Ihnen beispielsweise die Ausgabe des Befehls terraform plan.
Wie Sie sehen können, da wir nur zwei Ressourcen in unserer Konfiguration definiert haben, sehen wir einen Plan zur Erstellung von zwei Ressourcen.

7.4 Überprüfen, Iterieren und Genehmigen (oder Ablehnen) des Plans
An diesem Punkt kann mithilfe der nativen Github Branch Rules verlangt werden, dass eine Mindestanzahl von Prüfern die neuen Infrastrukturänderungen überprüft und genehmigt. Das Team kann ändern, was immer es braucht; neue Commits in den plan-Branch sammeln sich unter diesem Pull Request, lösen jedes Mal eine erneute Ausführung des plan workflows aus und stellen so sicher, dass das Team die aktuellsten Änderungen sieht, die beim nächsten Apply vorgenommen werden.
Da wir diesen Workflow bis zum Ende durchziehen und sicherstellen möchten, dass unsere Infrastruktur tatsächlich erstellt wird, gehen wir davon aus, dass das Team unsere neue Infrastruktur absolut großartig findet und die Verantwortlichen sie abgezeichnet haben. Wir werden daher fortfahren und diesen Pull Request #1 mergen und unsere Infrastruktur bereitstellen lassen.
7.5 Und... es ist Weihnachten

Ich mache ein rebasing, einfach weil ich eine aufgeräumte Commit-Historie mag.


Dies sollte unseren deploy-Workflow im main-Branch auslösen, und wie erwartet sollte es einwandfrei funktionieren, etwa so;


WIR HABEN ES GESCHAFFT!!! 🎉 🎉
Um zu bestätigen, dass unsere Infrastruktur in der realen Welt live ist, können wir ihre Webadresse besuchen. Meine war server.obi.ninja und sie funktionierte einwandfrei.

Ich habe https://, weil ich den kostenlosen SSL-Dienst von Cloudflare eingerichtet habe. Dies habe ich außerhalb von Terraform erledigt, als ich die Cloudflare-Zone selbst einrichtete, um dieses Projekt und diese Anleitung einfach zu halten.
8. Zusätzliche Hinweise
8.1 Github Branch Protection Rules
Um dieses Projekt kugelsicher zu machen, damit es für Ihre Organisation zuverlässig funktioniert, müssen Sie Github Branch Protection Rules verwenden. Mit diesen Regeln können Sie wichtige Dinge erzwingen, wie z.B.;
- Sicherstellen, dass die Branches
mainundplannicht gelöscht werden können - Einen Merge nach
mainnur zulassen, wenn sowohl dervalidate- als auch derplan-Workflow erfolgreich waren - Angabe der Mindestanzahl von Personen, die befugt sind, jede neue Änderung abzuzeichnen
- usw.
8.2 Zusätzliche Tools
Möglicherweise möchten Sie die verschiedenen Teile dieses Projekts um zusätzliche Tools erweitern, um die Sicherheit zu erhöhen. Tools wie:
- Hashicorp Vault für die Verwaltung von Geheimnissen
- Sonarqube für zusätzliche statische und Code-Qualitätsanalyse
- usw.
8.3 Zerstören der Infrastruktur
Dies ist ein Team-Workflow, und er ist das Rückgrat der Unternehmensinfrastruktur. Infolgedessen muss jede Zerstörung von Ressourcen durch Löschen oder Auskommentieren des Terraform-Codes für besagte Ressourcen erfolgen, und diese Änderung muss vom verantwortlichen Team überprüft werden. Niemand wird einfach in der Lage sein, terraform destroy auszuführen und Ressourcen zu zerstören, solange die Infrastrukturgeheimnisse ordnungsgemäß verwaltet werden.
8.4 Fazit
Ich hatte viel Spaß bei der Konzeption, Umsetzung und Dokumentation dieses Projekts. Ich hoffe, Sie finden es nützlich. Zögern Sie nicht, mir im Kommentarbereich unten mitzuteilen, was Sie denken, ob es Fehler gibt oder Raum für Verbesserungen besteht.
Mehr lesen
Self-Hosting-Projekt (DevOps-Infra from Scratch)
Ein umfassender Leitfaden zum Aufbau einer selbstgehosteten DevOps-Infrastruktur von Grund auf mit Terraform, Ansible und Docker.
Einen Server auf Digital Ocean über Terraform bereitstellen (HowTo)
Eine Schritt-für-Schritt-Anleitung zur Bereitstellung eines DigitalOcean-Droplets mit Terraform und grundlegenden Infrastructure as Code-Praktiken.
Auto-Deploy von Jenkins auf einem Remote-Server (mit kostenlosem SSL) über Ansible (HowTo)
Erfahren Sie, wie Sie einen selbst gehosteten Jenkins-Server mit kostenlosen, sich automatisch verlängernden SSL-Zertifikaten mithilfe von Ansible automatisch bereitstellen können.
