From 9172be62cabb0d0ba8337c3e62979ed6ba7ee2c6 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 11 May 2025 11:23:28 +0200 Subject: [PATCH 01/47] feat: add terraform files [skip ci] --- .github/workflows/ci-pipeline.yaml | 1 + .gitignore | 3 ++ README.md | 8 +++++ terraform/.terraform.lock.hcl | 22 ++++++++++++++ terraform/environments/dev.tfvars | 11 +++++++ terraform/environments/int.tfvars | 0 terraform/environments/prd.tfvars | 0 terraform/main.tf | 25 ++++++++++++++++ terraform/outputs.tf | 3 ++ terraform/providers.tf | 13 ++++++++ terraform/variables.tf | 48 ++++++++++++++++++++++++++++++ 11 files changed, 134 insertions(+) create mode 100644 terraform/.terraform.lock.hcl create mode 100644 terraform/environments/dev.tfvars create mode 100644 terraform/environments/int.tfvars create mode 100644 terraform/environments/prd.tfvars create mode 100644 terraform/main.tf create mode 100644 terraform/outputs.tf create mode 100644 terraform/providers.tf create mode 100644 terraform/variables.tf diff --git a/.github/workflows/ci-pipeline.yaml b/.github/workflows/ci-pipeline.yaml index e1d309b..19f206b 100644 --- a/.github/workflows/ci-pipeline.yaml +++ b/.github/workflows/ci-pipeline.yaml @@ -7,6 +7,7 @@ on: - 'feature/**' paths-ignore: - 'images/**' + - 'terraform/**' - '**/*.md' env: diff --git a/.gitignore b/.gitignore index 0a19790..73cd0c9 100644 --- a/.gitignore +++ b/.gitignore @@ -172,3 +172,6 @@ cython_debug/ # PyPI configuration file .pypirc + +# Terraform +terraform/.terraform \ No newline at end of file diff --git a/README.md b/README.md index 05ca98e..48bac0d 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,11 @@ A learning experience for setting up a CI/CD pipeline for Python - Create (separate?) pipeline to run continuous delivery on newly setup infrastructure - Check remaining TODO comments - Add project documentation + +## Infrastructure + +Source: https://learn.microsoft.com/en-us/azure/container-instances/container-instances-quickstart-terraform + +**How to manage Terraform state?** +- GitHub has no feature to store the Terraform state like GitLab +- I'll use an Azure Storage Account \ No newline at end of file diff --git a/terraform/.terraform.lock.hcl b/terraform/.terraform.lock.hcl new file mode 100644 index 0000000..eb0286e --- /dev/null +++ b/terraform/.terraform.lock.hcl @@ -0,0 +1,22 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/azurerm" { + version = "3.117.1" + constraints = "~> 3.0" + hashes = [ + "h1:3c9iOEtBMnHrpJLlhbQ0sCZPWhE/2dvEPcL8KkXAh7w=", + "zh:0c513676836e3c50d004ece7d2624a8aff6faac14b833b96feeac2e4bc2c1c12", + "zh:50ea01ada95bae2f187db9e926e463f45d860767a85ebc59160414e00e76c35d", + "zh:52c2a9edacc06b3f72153f5ef6daca0761c6292158815961fe37f60bc576a3d7", + "zh:618eed2a06b19b1a025b45b05891846d570a6a1cca4d23f4942f5a99e1f747ae", + "zh:61cde5d3165d7e5ec311d5d89486819cd605c1b2d54611b5c97bd4e97dba2762", + "zh:6a873358d5031fc222f5e05f029d1237f3dce8345c767665f393283dfa2627f6", + "zh:afdd80064b2a04da311856feb4ed45f77ff4df6c356e8c2b10afb51fe7e61c70", + "zh:b09113df7e0e8c8959539bd22bae6c39faeb269ba3c4cd948e742f5cf58c35fb", + "zh:d340db7973109761cfc27d52aa02560363337c908b2c99b3628adc5a70a99d5b", + "zh:d5a577226ebc8c65e8f19384878a86acc4b51ede4b4a82d37c3b331b0efcd4a7", + "zh:e2962b147f9e71732df8dbc74940c10d20906f3c003cbfaa1eb9fabbf601a9f0", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} diff --git a/terraform/environments/dev.tfvars b/terraform/environments/dev.tfvars new file mode 100644 index 0000000..a0d9c85 --- /dev/null +++ b/terraform/environments/dev.tfvars @@ -0,0 +1,11 @@ +resource_group_name = "maurice-sample-rg-dev" +resource_group_location = "westeurope" + +container_group_name = "maurice-sample-container-group-dev" +container_group_restart_policy = "Always" + +container_name = "python-cicd-container" +container_image = "TODO_IMAGE_NAME" +container_cpu_cores = 1 +container_memory_in_gb = 1 +container_port = 80 diff --git a/terraform/environments/int.tfvars b/terraform/environments/int.tfvars new file mode 100644 index 0000000..e69de29 diff --git a/terraform/environments/prd.tfvars b/terraform/environments/prd.tfvars new file mode 100644 index 0000000..e69de29 diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..afdce20 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,25 @@ +resource "azurerm_resource_group" "rg" { + name = var.resource_group_name + location = var.resource_group_location +} + +resource "azurerm_container_group" "container" { + name = var.container_group_name + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + ip_address_type = "Public" + os_type = "Linux" + restart_policy = var.container_group_restart_policy + + container { + name = var.container_name + image = var.container_image + cpu = var.container_cpu_cores + memory = var.container_memory_in_gb + + ports { + port = var.container_port + protocol = "TCP" + } + } +} diff --git a/terraform/outputs.tf b/terraform/outputs.tf new file mode 100644 index 0000000..5a07d7b --- /dev/null +++ b/terraform/outputs.tf @@ -0,0 +1,3 @@ +output "container_ipv4_address" { + value = azurerm_container_group.container.ip_address +} diff --git a/terraform/providers.tf b/terraform/providers.tf new file mode 100644 index 0000000..e43dab4 --- /dev/null +++ b/terraform/providers.tf @@ -0,0 +1,13 @@ +terraform { + required_version = ">=1.0" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~>3.0" + } + } +} + +provider "azurerm" { + features {} +} diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000..2981b2d --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,48 @@ +variable "resource_group_name" { + type = string + description = "Name of the resource group in which the project is deployed" +} + +variable "resource_group_location" { + type = string + description = "Location of the resource group" +} + +variable "container_group_name" { + type = string + description = "Name of the container group which runs the project image" +} + +variable "container_group_restart_policy" { + type = string + description = "The behavior of Azure runtime if container has stopped" + validation { + condition = contains(["Always", "Never", "OnFailure"], var.container_group_restart_policy) + error_message = "The restart_policy must be one of the following: Always, Never, OnFailure." + } +} + +variable "container_name" { + type = string + description = "Name of the container inside the container group which runs the project image" +} + +variable "container_image" { + type = string + description = "Name of the image that should be run" +} + +variable "container_cpu_cores" { + type = number + description = "Number of cpu cores the container will have available" +} + +variable "container_memory_in_gb" { + type = number + description = "Storage size in GB the container will have available" +} + +variable "container_port" { + type = number + description = "Port that the container exposes" +} From 94e133193e63d0c77aed12fe939e589b4e9956a6 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 11 May 2025 11:43:49 +0200 Subject: [PATCH 02/47] feat: include terraform checking in ci pipeline --- .github/workflows/ci-pipeline.yaml | 26 +++++++++++++++++++++++++- terraform/main.tf | 2 +- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-pipeline.yaml b/.github/workflows/ci-pipeline.yaml index 19f206b..6fb4cba 100644 --- a/.github/workflows/ci-pipeline.yaml +++ b/.github/workflows/ci-pipeline.yaml @@ -7,7 +7,6 @@ on: - 'feature/**' paths-ignore: - 'images/**' - - 'terraform/**' - '**/*.md' env: @@ -16,6 +15,31 @@ env: DOCKER_BUILD_SUMMARY: false # Deactivate build summary generation jobs: + check-infra-code: + name: Check Infrastructure Code + runs-on: ubuntu-latest + env: + TERRAFORM_ROOT_DIR: 'terraform' + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Init Terraform CLI + uses: hashicorp/setup-terraform@v3 + + - name: Run Format Check + run: | + cd $TERRAFORM_ROOT_DIR + terraform fmt -check -diff + + - name: Terraform Init + run: | + terraform init -input=false -backend=false + + - name: Run Validation + run: | + terraform validate + test-and-check: name: Test and Check Python Code runs-on: ubuntu-latest diff --git a/terraform/main.tf b/terraform/main.tf index afdce20..e3bad36 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -8,7 +8,7 @@ resource "azurerm_container_group" "container" { location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name ip_address_type = "Public" - os_type = "Linux" + os_type = "Linux" restart_policy = var.container_group_restart_policy container { From ab114623dff196e71aad1f4768e4bbc05a45bef4 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 11 May 2025 11:45:12 +0200 Subject: [PATCH 03/47] debug: test validity check --- terraform/main.tf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/terraform/main.tf b/terraform/main.tf index e3bad36..204c09d 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -8,8 +8,9 @@ resource "azurerm_container_group" "container" { location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name ip_address_type = "Public" - os_type = "Linux" + os_type = "Linux" restart_policy = var.container_group_restart_policy + some_none_existent_property = true container { name = var.container_name From 51098312df78fec2da24b9a0a731d6806ded098e Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 11 May 2025 11:46:05 +0200 Subject: [PATCH 04/47] debug: test validity check --- terraform/main.tf | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/terraform/main.tf b/terraform/main.tf index 204c09d..08b545a 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -4,12 +4,12 @@ resource "azurerm_resource_group" "rg" { } resource "azurerm_container_group" "container" { - name = var.container_group_name - location = azurerm_resource_group.rg.location - resource_group_name = azurerm_resource_group.rg.name - ip_address_type = "Public" - os_type = "Linux" - restart_policy = var.container_group_restart_policy + name = var.container_group_name + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + ip_address_type = "Public" + os_type = "Linux" + restart_policy = var.container_group_restart_policy some_none_existent_property = true container { From 4ac2953b4990de7f3636be045e75367b64028de7 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 11 May 2025 11:47:52 +0200 Subject: [PATCH 05/47] debug: print wd --- .github/workflows/ci-pipeline.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci-pipeline.yaml b/.github/workflows/ci-pipeline.yaml index 6fb4cba..b2fbae9 100644 --- a/.github/workflows/ci-pipeline.yaml +++ b/.github/workflows/ci-pipeline.yaml @@ -34,6 +34,7 @@ jobs: - name: Terraform Init run: | + pwd terraform init -input=false -backend=false - name: Run Validation From b61750c58d67bed08bb2262f804bdea96821df86 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 11 May 2025 11:50:51 +0200 Subject: [PATCH 06/47] fix: set default run working directory --- .github/workflows/ci-pipeline.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-pipeline.yaml b/.github/workflows/ci-pipeline.yaml index b2fbae9..a12e8c5 100644 --- a/.github/workflows/ci-pipeline.yaml +++ b/.github/workflows/ci-pipeline.yaml @@ -18,8 +18,9 @@ jobs: check-infra-code: name: Check Infrastructure Code runs-on: ubuntu-latest - env: - TERRAFORM_ROOT_DIR: 'terraform' + defaults: + run: + working-directory: ./terraform steps: - name: Checkout Repository uses: actions/checkout@v4 @@ -29,12 +30,10 @@ jobs: - name: Run Format Check run: | - cd $TERRAFORM_ROOT_DIR terraform fmt -check -diff - name: Terraform Init run: | - pwd terraform init -input=false -backend=false - name: Run Validation From fb45fb75452955f119b6e1ad6ce5bf9578ccff1b Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 11 May 2025 11:51:45 +0200 Subject: [PATCH 07/47] debug: disable validity check --- terraform/main.tf | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/terraform/main.tf b/terraform/main.tf index 08b545a..afdce20 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -4,13 +4,12 @@ resource "azurerm_resource_group" "rg" { } resource "azurerm_container_group" "container" { - name = var.container_group_name - location = azurerm_resource_group.rg.location - resource_group_name = azurerm_resource_group.rg.name - ip_address_type = "Public" - os_type = "Linux" - restart_policy = var.container_group_restart_policy - some_none_existent_property = true + name = var.container_group_name + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + ip_address_type = "Public" + os_type = "Linux" + restart_policy = var.container_group_restart_policy container { name = var.container_name From 9614a0ce8769aa66e1f02e544a0bdfcfcbe4beaa Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 11 May 2025 12:14:30 +0200 Subject: [PATCH 08/47] wip: add progress [skip ci] --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 48bac0d..4596138 100644 --- a/README.md +++ b/README.md @@ -14,4 +14,5 @@ Source: https://learn.microsoft.com/en-us/azure/container-instances/container-in **How to manage Terraform state?** - GitHub has no feature to store the Terraform state like GitLab -- I'll use an Azure Storage Account \ No newline at end of file +- I'll use an Azure Storage Account +- https://developer.hashicorp.com/terraform/language/backend/azurerm#azure-active-directory \ No newline at end of file From 35cf3f258f9c264fee1d3807155402fb161051e3 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 11 May 2025 12:15:16 +0200 Subject: [PATCH 09/47] wip: add progress [skip ci] --- .github/workflows/infra-pipeline.yaml | 27 +++++++++++++++++++++++++++ terraform/backend.tf | 9 +++++++++ 2 files changed, 36 insertions(+) create mode 100644 .github/workflows/infra-pipeline.yaml create mode 100644 terraform/backend.tf diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml new file mode 100644 index 0000000..72a071b --- /dev/null +++ b/.github/workflows/infra-pipeline.yaml @@ -0,0 +1,27 @@ +name: Infrastructure Pipeline + +on: + push: + branches: + - main + +jobs: + run-tf: + name: "Run Terraform Code" + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./terraform + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Init Terraform CLI + uses: hashicorp/setup-terraform@v3 + + - name: Terraform Init + run: | + ARM_USE_AZUREAD=true + ARM_TENANT_ID=TODO_TENANT_ID_HERE + ARM_ACCESS_KEY=${{ secrets.ARM_ACCESS_KEY }} + terraform init \ No newline at end of file diff --git a/terraform/backend.tf b/terraform/backend.tf new file mode 100644 index 0000000..84b1077 --- /dev/null +++ b/terraform/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "azurerm" { + use_oidc = true + use_azuread_auth = true + storage_account_name = "samauriceatropsdev" # TODO: How to account for different environments? + container_name = "tfstate" + key = "terraform.tfstate" + } +} From 69deed744e54cab5173c2c316f1d08112088b02e Mon Sep 17 00:00:00 2001 From: Maurice Date: Sat, 17 May 2025 08:45:42 +0200 Subject: [PATCH 10/47] feat: add oidc authentication for user-managed identity --- .github/workflows/infra-pipeline.yaml | 3 --- README.md | 4 +++- terraform/backend.tf | 3 +++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index 72a071b..321da1b 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -21,7 +21,4 @@ jobs: - name: Terraform Init run: | - ARM_USE_AZUREAD=true - ARM_TENANT_ID=TODO_TENANT_ID_HERE - ARM_ACCESS_KEY=${{ secrets.ARM_ACCESS_KEY }} terraform init \ No newline at end of file diff --git a/README.md b/README.md index 4596138..2bc4a0a 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,10 @@ A learning experience for setting up a CI/CD pipeline for Python ## Infrastructure Source: https://learn.microsoft.com/en-us/azure/container-instances/container-instances-quickstart-terraform +- Created managed identity `infra-user-dev` +- Use Federated Identity Credentials to enable OIDC and avoid managing (and rotating) secrets **How to manage Terraform state?** - GitHub has no feature to store the Terraform state like GitLab - I'll use an Azure Storage Account -- https://developer.hashicorp.com/terraform/language/backend/azurerm#azure-active-directory \ No newline at end of file +- https://developer.hashicorp.com/terraform/language/backend/azurerm#azure-active-directory diff --git a/terraform/backend.tf b/terraform/backend.tf index 84b1077..b6b3496 100644 --- a/terraform/backend.tf +++ b/terraform/backend.tf @@ -1,7 +1,10 @@ terraform { + # TODO: Environment agnostic! backend "azurerm" { use_oidc = true use_azuread_auth = true + tenant_id = "e5f140b4-77b2-48a0-9775-fe7437c50fa6" + client_id = "c3971fe8-fae4-4df5-91f3-4b2e71078ffe" storage_account_name = "samauriceatropsdev" # TODO: How to account for different environments? container_name = "tfstate" key = "terraform.tfstate" From a0af4ed14a7efc1a7eb9bd99aa38202653342e22 Mon Sep 17 00:00:00 2001 From: Maurice Date: Sat, 17 May 2025 08:49:46 +0200 Subject: [PATCH 11/47] feat: add oidc authentication for user-managed identity --- .github/workflows/infra-pipeline.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index 321da1b..6ab8e96 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -4,6 +4,7 @@ on: push: branches: - main + - 'feature/**' jobs: run-tf: From 17547eee50c739e81033c7215501650684a24f4e Mon Sep 17 00:00:00 2001 From: Maurice Date: Sat, 17 May 2025 08:57:10 +0200 Subject: [PATCH 12/47] fix: whitelist paths --- .github/workflows/ci-pipeline.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-pipeline.yaml b/.github/workflows/ci-pipeline.yaml index a12e8c5..a1d6dbe 100644 --- a/.github/workflows/ci-pipeline.yaml +++ b/.github/workflows/ci-pipeline.yaml @@ -5,9 +5,12 @@ on: branches: - main - 'feature/**' - paths-ignore: - - 'images/**' - - '**/*.md' + paths: + - 'src/**/*' + - 'tests/**/*' + - 'terraform/**/*' + - 'Dockerfile' + - 'pyproject.toml' env: PYTHON_IMAGE: 'python:3.12-slim' From 6abed824b9ac51bcac29d6cb6225a729624f3998 Mon Sep 17 00:00:00 2001 From: Maurice Date: Sat, 17 May 2025 08:57:21 +0200 Subject: [PATCH 13/47] fix: add azure cli login --- .github/workflows/infra-pipeline.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index 6ab8e96..90434c0 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -17,6 +17,13 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 + - name: Azure Login + uses: azure/login@v2 + with: + client-id: c3971fe8-fae4-4df5-91f3-4b2e71078ffe + tenant-id: e5f140b4-77b2-48a0-9775-fe7437c50fa6 + subscription-id: 486b8df7-bf37-4b89-a5ee-c43cf5ec2b05 + - name: Init Terraform CLI uses: hashicorp/setup-terraform@v3 From a73e49de3daeae71ea0bd2d61012910a501baff6 Mon Sep 17 00:00:00 2001 From: Maurice Date: Sat, 17 May 2025 09:01:38 +0200 Subject: [PATCH 14/47] fix: add permissions --- .github/workflows/ci-pipeline.yaml | 1 + .github/workflows/infra-pipeline.yaml | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/ci-pipeline.yaml b/.github/workflows/ci-pipeline.yaml index a1d6dbe..f39c3c8 100644 --- a/.github/workflows/ci-pipeline.yaml +++ b/.github/workflows/ci-pipeline.yaml @@ -6,6 +6,7 @@ on: - main - 'feature/**' paths: + - '.github/workflows/ci-pipeline.yaml' - 'src/**/*' - 'tests/**/*' - 'terraform/**/*' diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index 90434c0..dc2efdb 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -5,6 +5,12 @@ on: branches: - main - 'feature/**' + paths: + - '.github/workflows/infra-pipeline.yaml' + - 'terraform/**/*' + +permissions: + id-token: write # Needed for Azure CLI Login jobs: run-tf: From aa3cd70420cb3110c3686be25eaa60514c5335f1 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sat, 17 May 2025 12:09:04 +0200 Subject: [PATCH 15/47] fix: remove secrets from backend.tf --- .github/workflows/infra-pipeline.yaml | 10 ++++++---- README.md | 3 +++ terraform/backend.tf | 2 -- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index dc2efdb..79b5bb6 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -26,13 +26,15 @@ jobs: - name: Azure Login uses: azure/login@v2 with: - client-id: c3971fe8-fae4-4df5-91f3-4b2e71078ffe - tenant-id: e5f140b4-77b2-48a0-9775-fe7437c50fa6 - subscription-id: 486b8df7-bf37-4b89-a5ee-c43cf5ec2b05 + client-id: ${{ secrets.INFRA_ARM_CLIENT_ID }} + tenant-id: ${{ secrets.ARM_TENANT_ID }} + subscription-id: ${{ secrets.ARM_SUBSCRIPTION_ID }} - name: Init Terraform CLI uses: hashicorp/setup-terraform@v3 - name: Terraform Init run: | - terraform init \ No newline at end of file + terraform init \ + --backend-config=ARM_CLIENT_ID=${{ secrets.INFRA_ARM_CLIENT_ID }} + --backend-config=ARM_TENANT_ID=${{ secrets.ARM_TENANT_ID }} \ No newline at end of file diff --git a/README.md b/README.md index 2bc4a0a..637e0fe 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,9 @@ A learning experience for setting up a CI/CD pipeline for Python Source: https://learn.microsoft.com/en-us/azure/container-instances/container-instances-quickstart-terraform - Created managed identity `infra-user-dev` - Use Federated Identity Credentials to enable OIDC and avoid managing (and rotating) secrets +- Problem: Azure needs exact branch path for credential (no wildcard) + - Seems to be by design: https://learn.microsoft.com/en-us/answers/questions/2073829/azure-github-action-federated-identity-login-issue +- TODO: Research how OIDC works exactly **How to manage Terraform state?** - GitHub has no feature to store the Terraform state like GitLab diff --git a/terraform/backend.tf b/terraform/backend.tf index b6b3496..c497df6 100644 --- a/terraform/backend.tf +++ b/terraform/backend.tf @@ -3,8 +3,6 @@ terraform { backend "azurerm" { use_oidc = true use_azuread_auth = true - tenant_id = "e5f140b4-77b2-48a0-9775-fe7437c50fa6" - client_id = "c3971fe8-fae4-4df5-91f3-4b2e71078ffe" storage_account_name = "samauriceatropsdev" # TODO: How to account for different environments? container_name = "tfstate" key = "terraform.tfstate" From 5455fad089a433f4a5806fb0a648f60d6e130cb2 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sat, 17 May 2025 12:12:25 +0200 Subject: [PATCH 16/47] fix: remove secrets from backend.tf [skip ci] --- .github/workflows/infra-pipeline.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index 79b5bb6..6d65249 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -36,5 +36,5 @@ jobs: - name: Terraform Init run: | terraform init \ - --backend-config=ARM_CLIENT_ID=${{ secrets.INFRA_ARM_CLIENT_ID }} - --backend-config=ARM_TENANT_ID=${{ secrets.ARM_TENANT_ID }} \ No newline at end of file + --backend-config=client_id=${{ secrets.INFRA_ARM_CLIENT_ID }} + --backend-config=tenant_id=${{ secrets.ARM_TENANT_ID }} \ No newline at end of file From a8a69491514546c7a1951372263d70af793124aa Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sat, 17 May 2025 12:14:34 +0200 Subject: [PATCH 17/47] fix: add manual trigger to infra pipeline --- .github/workflows/infra-pipeline.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index 6d65249..589d3ba 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -1,6 +1,7 @@ name: Infrastructure Pipeline on: + workflow_dispatch push: branches: - main From 7c91681ce0cb8eed6f8adfa17d3064e4c02150aa Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sat, 17 May 2025 12:15:05 +0200 Subject: [PATCH 18/47] fix: add manual trigger to infra pipeline --- .github/workflows/infra-pipeline.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index 589d3ba..229f76e 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -1,7 +1,7 @@ name: Infrastructure Pipeline on: - workflow_dispatch + workflow_dispatch: push: branches: - main From dab99e40a4e48e56fe11418d70508c1efdb477b9 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sat, 17 May 2025 12:16:49 +0200 Subject: [PATCH 19/47] fix: syntax --- .github/workflows/infra-pipeline.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index 229f76e..5cbed0d 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -1,7 +1,6 @@ name: Infrastructure Pipeline on: - workflow_dispatch: push: branches: - main @@ -37,5 +36,5 @@ jobs: - name: Terraform Init run: | terraform init \ - --backend-config=client_id=${{ secrets.INFRA_ARM_CLIENT_ID }} + --backend-config=client_id=${{ secrets.INFRA_ARM_CLIENT_ID }} \ --backend-config=tenant_id=${{ secrets.ARM_TENANT_ID }} \ No newline at end of file From e6c74acf82fa923e9a93189687e7d35cdf577c09 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sat, 17 May 2025 12:25:49 +0200 Subject: [PATCH 20/47] feat: add code to run terraform plan and apply --- .github/workflows/infra-pipeline.yaml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index 5cbed0d..c8784dc 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -12,6 +12,10 @@ on: permissions: id-token: write # Needed for Azure CLI Login +env: + TF_ENV_VARS_FILE_PATH: 'terraform/environments/dev.tfvars' + TF_PLAN_FILE_PATH: '/tmp/tf_plan' + jobs: run-tf: name: "Run Terraform Code" @@ -37,4 +41,13 @@ jobs: run: | terraform init \ --backend-config=client_id=${{ secrets.INFRA_ARM_CLIENT_ID }} \ - --backend-config=tenant_id=${{ secrets.ARM_TENANT_ID }} \ No newline at end of file + --backend-config=tenant_id=${{ secrets.ARM_TENANT_ID }} + + - name: Terraform Plan + run: | + terraform plan -var-file=$TF_ENV_VARS_FILE_PATH -out=$TF_PLAN_FILE_PATH + + - name: Terraform Apply + if: github.ref_name == 'main' + run: | + terraform apply -var-file=$TF_ENV_VARS_FILE_PATH -auto-approve $TF_PLAN_FILE_PATH From 59d521d62ce44e2b49250aa8b418ae7394fe7fca Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sat, 17 May 2025 12:27:17 +0200 Subject: [PATCH 21/47] debug: add pwd --- .github/workflows/infra-pipeline.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index c8784dc..b921738 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -45,6 +45,7 @@ jobs: - name: Terraform Plan run: | + pwd terraform plan -var-file=$TF_ENV_VARS_FILE_PATH -out=$TF_PLAN_FILE_PATH - name: Terraform Apply From f0cc4845e8e16b5fd86b28fe1f6c7b1327024f21 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sat, 17 May 2025 12:28:29 +0200 Subject: [PATCH 22/47] fix: tf vars path --- .github/workflows/infra-pipeline.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index b921738..813673e 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -13,7 +13,7 @@ permissions: id-token: write # Needed for Azure CLI Login env: - TF_ENV_VARS_FILE_PATH: 'terraform/environments/dev.tfvars' + TF_ENV_VARS_FILE_PATH: 'environments/dev.tfvars' TF_PLAN_FILE_PATH: '/tmp/tf_plan' jobs: @@ -45,7 +45,6 @@ jobs: - name: Terraform Plan run: | - pwd terraform plan -var-file=$TF_ENV_VARS_FILE_PATH -out=$TF_PLAN_FILE_PATH - name: Terraform Apply From efdfb645f44ddc09becc48af8f3892e9b8b26aed Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sat, 17 May 2025 12:32:48 +0200 Subject: [PATCH 23/47] debug: try creating infrastructure --- .github/workflows/infra-pipeline.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index 813673e..9bb07c3 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -48,6 +48,6 @@ jobs: terraform plan -var-file=$TF_ENV_VARS_FILE_PATH -out=$TF_PLAN_FILE_PATH - name: Terraform Apply - if: github.ref_name == 'main' + # if: github.ref_name == 'main' run: | terraform apply -var-file=$TF_ENV_VARS_FILE_PATH -auto-approve $TF_PLAN_FILE_PATH From c7904e4b18e2b74b1a8b548edea7f21ec3151941 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sat, 17 May 2025 12:39:18 +0200 Subject: [PATCH 24/47] fix: update rg name --- README.md | 1 + terraform/environments/dev.tfvars | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 637e0fe..ab91e07 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Source: https://learn.microsoft.com/en-us/azure/container-instances/container-in - Problem: Azure needs exact branch path for credential (no wildcard) - Seems to be by design: https://learn.microsoft.com/en-us/answers/questions/2073829/azure-github-action-federated-identity-login-issue - TODO: Research how OIDC works exactly +- TODO: Explain that I will not separate environments here, though for real-life it would be mandatory **How to manage Terraform state?** - GitHub has no feature to store the Terraform state like GitLab diff --git a/terraform/environments/dev.tfvars b/terraform/environments/dev.tfvars index a0d9c85..210cc9b 100644 --- a/terraform/environments/dev.tfvars +++ b/terraform/environments/dev.tfvars @@ -1,4 +1,4 @@ -resource_group_name = "maurice-sample-rg-dev" +resource_group_name = "mauriceatrops-dev" resource_group_location = "westeurope" container_group_name = "maurice-sample-container-group-dev" From 64499629fd75a4da7d8a8dba7bd9ceb172fe24d8 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sat, 17 May 2025 12:42:00 +0200 Subject: [PATCH 25/47] fix: import existing resource group --- README.md | 1 + terraform/data.tf | 4 ++++ terraform/main.tf | 9 ++------- 3 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 terraform/data.tf diff --git a/README.md b/README.md index ab91e07..58fc78f 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Source: https://learn.microsoft.com/en-us/azure/container-instances/container-in - Seems to be by design: https://learn.microsoft.com/en-us/answers/questions/2073829/azure-github-action-federated-identity-login-issue - TODO: Research how OIDC works exactly - TODO: Explain that I will not separate environments here, though for real-life it would be mandatory +- TODO: Main resource group already exists in terraform setup **How to manage Terraform state?** - GitHub has no feature to store the Terraform state like GitLab diff --git a/terraform/data.tf b/terraform/data.tf new file mode 100644 index 0000000..ac89d1b --- /dev/null +++ b/terraform/data.tf @@ -0,0 +1,4 @@ +data "azurerm_resource_group" "rg" { + name = var.resource_group_name + location = var.resource_group_location +} \ No newline at end of file diff --git a/terraform/main.tf b/terraform/main.tf index afdce20..0c2e14f 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -1,12 +1,7 @@ -resource "azurerm_resource_group" "rg" { - name = var.resource_group_name - location = var.resource_group_location -} - resource "azurerm_container_group" "container" { name = var.container_group_name - location = azurerm_resource_group.rg.location - resource_group_name = azurerm_resource_group.rg.name + location = data.azurerm_resource_group.rg.location + resource_group_name = data.azurerm_resource_group.rg.name ip_address_type = "Public" os_type = "Linux" restart_policy = var.container_group_restart_policy From e01a6c57c99df80efbb0b251d2935f25983e2b84 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sat, 17 May 2025 12:45:00 +0200 Subject: [PATCH 26/47] fix: remove location property --- terraform/data.tf | 1 - 1 file changed, 1 deletion(-) diff --git a/terraform/data.tf b/terraform/data.tf index ac89d1b..8ed9fc0 100644 --- a/terraform/data.tf +++ b/terraform/data.tf @@ -1,4 +1,3 @@ data "azurerm_resource_group" "rg" { name = var.resource_group_name - location = var.resource_group_location } \ No newline at end of file From 707d964f2d7f77286d35d3585cdfd94cb7745b84 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sat, 17 May 2025 12:45:17 +0200 Subject: [PATCH 27/47] fix: remove location property --- terraform/data.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/data.tf b/terraform/data.tf index 8ed9fc0..641213c 100644 --- a/terraform/data.tf +++ b/terraform/data.tf @@ -1,3 +1,3 @@ data "azurerm_resource_group" "rg" { - name = var.resource_group_name + name = var.resource_group_name } \ No newline at end of file From 236eb6f3975035a3ffff342f6fd8699a502e2a09 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 14:36:00 +0200 Subject: [PATCH 28/47] feat: replace image name by real one [skip ci] --- .github/workflows/infra-pipeline.yaml | 6 +++++- terraform/environments/dev.tfvars | 2 +- terraform/main.tf | 2 +- terraform/variables.tf | 7 +++++++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index 9bb07c3..a03dec1 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -50,4 +50,8 @@ jobs: - name: Terraform Apply # if: github.ref_name == 'main' run: | - terraform apply -var-file=$TF_ENV_VARS_FILE_PATH -auto-approve $TF_PLAN_FILE_PATH + terraform apply \ + -var-file=$TF_ENV_VARS_FILE_PATH \ + -var 'dockerhub_username=${{ secrets.DOCKERHUB_USERNAME }}' + -auto-approve \ + $TF_PLAN_FILE_PATH diff --git a/terraform/environments/dev.tfvars b/terraform/environments/dev.tfvars index 210cc9b..d06f676 100644 --- a/terraform/environments/dev.tfvars +++ b/terraform/environments/dev.tfvars @@ -5,7 +5,7 @@ container_group_name = "maurice-sample-container-group-dev" container_group_restart_policy = "Always" container_name = "python-cicd-container" -container_image = "TODO_IMAGE_NAME" +container_image = "python-cicd" container_cpu_cores = 1 container_memory_in_gb = 1 container_port = 80 diff --git a/terraform/main.tf b/terraform/main.tf index 0c2e14f..c854607 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -8,7 +8,7 @@ resource "azurerm_container_group" "container" { container { name = var.container_name - image = var.container_image + image = "${var.dockerhub_username}/${var.container_image}" cpu = var.container_cpu_cores memory = var.container_memory_in_gb diff --git a/terraform/variables.tf b/terraform/variables.tf index 2981b2d..332ce23 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -46,3 +46,10 @@ variable "container_port" { type = number description = "Port that the container exposes" } + +variable "dockerhub_username" { + type = string + sensitive = true + default = "n/a" + description = "Username (and repo name) for the image repository on Dockerhub" +} \ No newline at end of file From a69dbc7be7103523bbb32ef003c6258af3d48f94 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 14:36:53 +0200 Subject: [PATCH 29/47] fix: pipeline syntax --- .github/workflows/infra-pipeline.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index a03dec1..fc38632 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -52,6 +52,6 @@ jobs: run: | terraform apply \ -var-file=$TF_ENV_VARS_FILE_PATH \ - -var 'dockerhub_username=${{ secrets.DOCKERHUB_USERNAME }}' + -var 'dockerhub_username=${{ secrets.DOCKERHUB_USERNAME }}' \ -auto-approve \ $TF_PLAN_FILE_PATH From 2cbb129df68f53eea2eeda3c97c57647018048f4 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 14:40:10 +0200 Subject: [PATCH 30/47] fix: add dockerhub username to tf plan --- .github/workflows/infra-pipeline.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index fc38632..e068c44 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -45,7 +45,10 @@ jobs: - name: Terraform Plan run: | - terraform plan -var-file=$TF_ENV_VARS_FILE_PATH -out=$TF_PLAN_FILE_PATH + terraform plan \ + -var-file=$TF_ENV_VARS_FILE_PATH \ + -var 'dockerhub_username=${{ secrets.DOCKERHUB_USERNAME }}' \ + -out=$TF_PLAN_FILE_PATH - name: Terraform Apply # if: github.ref_name == 'main' From 65229cf4314d49c97cee22e5a1b745f22f0e5517 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 14:46:01 +0200 Subject: [PATCH 31/47] debug: add tag to docker image --- terraform/environments/dev.tfvars | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/environments/dev.tfvars b/terraform/environments/dev.tfvars index d06f676..a54b9b0 100644 --- a/terraform/environments/dev.tfvars +++ b/terraform/environments/dev.tfvars @@ -5,7 +5,7 @@ container_group_name = "maurice-sample-container-group-dev" container_group_restart_policy = "Always" container_name = "python-cicd-container" -container_image = "python-cicd" +container_image = "python-cicd:latest" container_cpu_cores = 1 container_memory_in_gb = 1 container_port = 80 From b3cbba8845bc979a027e707c6dc12dbe34da1c2f Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 14:51:34 +0200 Subject: [PATCH 32/47] debug: add authentication to dockerhub --- .github/workflows/infra-pipeline.yaml | 2 ++ terraform/main.tf | 6 ++++++ terraform/variables.tf | 9 +++++++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index e068c44..a70eebc 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -48,6 +48,7 @@ jobs: terraform plan \ -var-file=$TF_ENV_VARS_FILE_PATH \ -var 'dockerhub_username=${{ secrets.DOCKERHUB_USERNAME }}' \ + -var 'dockerhub_password=${{ secrets.DOCKERHUB_PASSWORD }}' \ -out=$TF_PLAN_FILE_PATH - name: Terraform Apply @@ -56,5 +57,6 @@ jobs: terraform apply \ -var-file=$TF_ENV_VARS_FILE_PATH \ -var 'dockerhub_username=${{ secrets.DOCKERHUB_USERNAME }}' \ + -var 'dockerhub_password=${{ secrets.DOCKERHUB_PASSWORD }}' \ -auto-approve \ $TF_PLAN_FILE_PATH diff --git a/terraform/main.tf b/terraform/main.tf index c854607..b5e19c6 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -17,4 +17,10 @@ resource "azurerm_container_group" "container" { protocol = "TCP" } } + + image_registry_credential { + server = "index.docker.io" + username = var.dockerhub_username + password = var.dockerhub_password + } } diff --git a/terraform/variables.tf b/terraform/variables.tf index 332ce23..0785f87 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -50,6 +50,11 @@ variable "container_port" { variable "dockerhub_username" { type = string sensitive = true - default = "n/a" description = "Username (and repo name) for the image repository on Dockerhub" -} \ No newline at end of file +} + +variable "dockerhub_password" { + type = string + sensitive = true + description = "Pasword for the image repository on Dockerhub" +} From 3c643795b710db1c471ead4986d534c1a31da5e4 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 14:53:39 +0200 Subject: [PATCH 33/47] fix: variable name --- .github/workflows/infra-pipeline.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/infra-pipeline.yaml index a70eebc..3ee461c 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/infra-pipeline.yaml @@ -48,7 +48,7 @@ jobs: terraform plan \ -var-file=$TF_ENV_VARS_FILE_PATH \ -var 'dockerhub_username=${{ secrets.DOCKERHUB_USERNAME }}' \ - -var 'dockerhub_password=${{ secrets.DOCKERHUB_PASSWORD }}' \ + -var 'dockerhub_password=${{ secrets.DOCKERHUB_TOKEN }}' \ -out=$TF_PLAN_FILE_PATH - name: Terraform Apply @@ -57,6 +57,6 @@ jobs: terraform apply \ -var-file=$TF_ENV_VARS_FILE_PATH \ -var 'dockerhub_username=${{ secrets.DOCKERHUB_USERNAME }}' \ - -var 'dockerhub_password=${{ secrets.DOCKERHUB_PASSWORD }}' \ + -var 'dockerhub_password=${{ secrets.DOCKERHUB_TOKEN }}' \ -auto-approve \ $TF_PLAN_FILE_PATH From e88ee532ac21e17c63760f5a265d4730829a52ac Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 15:01:22 +0200 Subject: [PATCH 34/47] fix: open another container port --- terraform/environments/dev.tfvars | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/environments/dev.tfvars b/terraform/environments/dev.tfvars index a54b9b0..5685340 100644 --- a/terraform/environments/dev.tfvars +++ b/terraform/environments/dev.tfvars @@ -8,4 +8,4 @@ container_name = "python-cicd-container" container_image = "python-cicd:latest" container_cpu_cores = 1 container_memory_in_gb = 1 -container_port = 80 +container_port = 8080 From 2c959a1f80f1b2dff9bb664dc51ce050eea9a61a Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 15:12:02 +0200 Subject: [PATCH 35/47] feat: add infra destruction pipeline [skip ci] --- ...peline.yaml => create-infra-pipeline.yaml} | 2 +- .github/workflows/destroy-infra-pipeline.yaml | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) rename .github/workflows/{infra-pipeline.yaml => create-infra-pipeline.yaml} (97%) create mode 100644 .github/workflows/destroy-infra-pipeline.yaml diff --git a/.github/workflows/infra-pipeline.yaml b/.github/workflows/create-infra-pipeline.yaml similarity index 97% rename from .github/workflows/infra-pipeline.yaml rename to .github/workflows/create-infra-pipeline.yaml index 3ee461c..1c9c7fc 100644 --- a/.github/workflows/infra-pipeline.yaml +++ b/.github/workflows/create-infra-pipeline.yaml @@ -1,4 +1,4 @@ -name: Infrastructure Pipeline +name: Create Infrastructure Pipeline on: push: diff --git a/.github/workflows/destroy-infra-pipeline.yaml b/.github/workflows/destroy-infra-pipeline.yaml new file mode 100644 index 0000000..9f7d2f8 --- /dev/null +++ b/.github/workflows/destroy-infra-pipeline.yaml @@ -0,0 +1,38 @@ +name: Destroy Infrastructure Pipeline + +on: + workflow_dispatch: + +permissions: + id-token: write # Needed for Azure CLI Login + +jobs: + run-tf: + name: "Run Terraform Code" + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./terraform + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.INFRA_ARM_CLIENT_ID }} + tenant-id: ${{ secrets.ARM_TENANT_ID }} + subscription-id: ${{ secrets.ARM_SUBSCRIPTION_ID }} + + - name: Init Terraform CLI + uses: hashicorp/setup-terraform@v3 + + - name: Terraform Init + run: | + terraform init \ + --backend-config=client_id=${{ secrets.INFRA_ARM_CLIENT_ID }} \ + --backend-config=tenant_id=${{ secrets.ARM_TENANT_ID }} + + - name: Terraform Destroy + run: | + terraform destroy From 52adec3a8e611c6cae71aae8d4795763c41e9ce6 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 15:13:40 +0200 Subject: [PATCH 36/47] debug: run destruction pipeline --- .github/workflows/destroy-infra-pipeline.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/destroy-infra-pipeline.yaml b/.github/workflows/destroy-infra-pipeline.yaml index 9f7d2f8..04c493f 100644 --- a/.github/workflows/destroy-infra-pipeline.yaml +++ b/.github/workflows/destroy-infra-pipeline.yaml @@ -2,6 +2,9 @@ name: Destroy Infrastructure Pipeline on: workflow_dispatch: + push: + branches: + - 'feature/**' permissions: id-token: write # Needed for Azure CLI Login From e37538e148f2d7cf263a0578a3062c6849cfef16 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 17:37:17 +0200 Subject: [PATCH 37/47] fix: add variable file to destroy --- .github/workflows/destroy-infra-pipeline.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/destroy-infra-pipeline.yaml b/.github/workflows/destroy-infra-pipeline.yaml index 04c493f..91c0e5d 100644 --- a/.github/workflows/destroy-infra-pipeline.yaml +++ b/.github/workflows/destroy-infra-pipeline.yaml @@ -38,4 +38,5 @@ jobs: - name: Terraform Destroy run: | - terraform destroy + terraform destroy \ + -var-file=$TF_ENV_VARS_FILE_PATH From 80475db1028d49219e308950b8dd1404cdb54550 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 17:38:20 +0200 Subject: [PATCH 38/47] fix: add variable file to destroy --- .github/workflows/destroy-infra-pipeline.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/destroy-infra-pipeline.yaml b/.github/workflows/destroy-infra-pipeline.yaml index 91c0e5d..70b69c0 100644 --- a/.github/workflows/destroy-infra-pipeline.yaml +++ b/.github/workflows/destroy-infra-pipeline.yaml @@ -9,6 +9,9 @@ on: permissions: id-token: write # Needed for Azure CLI Login +env: + TF_ENV_VARS_FILE_PATH: 'environments/dev.tfvars' + jobs: run-tf: name: "Run Terraform Code" From d6ae1b220f0e3313674f67b1d90abff283e91922 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 17:40:43 +0200 Subject: [PATCH 39/47] fix: add variable file to destroy --- .github/workflows/destroy-infra-pipeline.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/destroy-infra-pipeline.yaml b/.github/workflows/destroy-infra-pipeline.yaml index 70b69c0..0ffea67 100644 --- a/.github/workflows/destroy-infra-pipeline.yaml +++ b/.github/workflows/destroy-infra-pipeline.yaml @@ -43,3 +43,5 @@ jobs: run: | terraform destroy \ -var-file=$TF_ENV_VARS_FILE_PATH + -var 'dockerhub_username=${{ secrets.DOCKERHUB_USERNAME }}' \ + -var 'dockerhub_password=${{ secrets.DOCKERHUB_TOKEN }}' From 091a5187060a7d4d94bdb75be7fb334f6edb723f Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 17:44:44 +0200 Subject: [PATCH 40/47] fix: add variable file to destroy --- .github/workflows/destroy-infra-pipeline.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/destroy-infra-pipeline.yaml b/.github/workflows/destroy-infra-pipeline.yaml index 0ffea67..36bfef6 100644 --- a/.github/workflows/destroy-infra-pipeline.yaml +++ b/.github/workflows/destroy-infra-pipeline.yaml @@ -42,6 +42,6 @@ jobs: - name: Terraform Destroy run: | terraform destroy \ - -var-file=$TF_ENV_VARS_FILE_PATH + -var-file=$TF_ENV_VARS_FILE_PATH \ -var 'dockerhub_username=${{ secrets.DOCKERHUB_USERNAME }}' \ -var 'dockerhub_password=${{ secrets.DOCKERHUB_TOKEN }}' From 75243ae3c97e930466223d30fdccfacca7f46f62 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 18:02:22 +0200 Subject: [PATCH 41/47] fix: add auto approve --- .github/workflows/destroy-infra-pipeline.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/destroy-infra-pipeline.yaml b/.github/workflows/destroy-infra-pipeline.yaml index 36bfef6..aea8e1e 100644 --- a/.github/workflows/destroy-infra-pipeline.yaml +++ b/.github/workflows/destroy-infra-pipeline.yaml @@ -44,4 +44,5 @@ jobs: terraform destroy \ -var-file=$TF_ENV_VARS_FILE_PATH \ -var 'dockerhub_username=${{ secrets.DOCKERHUB_USERNAME }}' \ - -var 'dockerhub_password=${{ secrets.DOCKERHUB_TOKEN }}' + -var 'dockerhub_password=${{ secrets.DOCKERHUB_TOKEN }}' \ + -auto-approve From a23494ac4e6421a1c24d557c517999d5c37536a4 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 18:05:51 +0200 Subject: [PATCH 42/47] debug: remove debugging options --- .github/workflows/create-infra-pipeline.yaml | 2 +- .github/workflows/destroy-infra-pipeline.yaml | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/create-infra-pipeline.yaml b/.github/workflows/create-infra-pipeline.yaml index 1c9c7fc..f58fef7 100644 --- a/.github/workflows/create-infra-pipeline.yaml +++ b/.github/workflows/create-infra-pipeline.yaml @@ -52,7 +52,7 @@ jobs: -out=$TF_PLAN_FILE_PATH - name: Terraform Apply - # if: github.ref_name == 'main' + if: github.ref_name == 'main' run: | terraform apply \ -var-file=$TF_ENV_VARS_FILE_PATH \ diff --git a/.github/workflows/destroy-infra-pipeline.yaml b/.github/workflows/destroy-infra-pipeline.yaml index aea8e1e..eed5a00 100644 --- a/.github/workflows/destroy-infra-pipeline.yaml +++ b/.github/workflows/destroy-infra-pipeline.yaml @@ -2,9 +2,6 @@ name: Destroy Infrastructure Pipeline on: workflow_dispatch: - push: - branches: - - 'feature/**' permissions: id-token: write # Needed for Azure CLI Login From 89103e7213c44e66358d35a0535038e2df682a35 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 18:06:18 +0200 Subject: [PATCH 43/47] fix: filename --- .github/workflows/create-infra-pipeline.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create-infra-pipeline.yaml b/.github/workflows/create-infra-pipeline.yaml index f58fef7..57c22c3 100644 --- a/.github/workflows/create-infra-pipeline.yaml +++ b/.github/workflows/create-infra-pipeline.yaml @@ -6,7 +6,7 @@ on: - main - 'feature/**' paths: - - '.github/workflows/infra-pipeline.yaml' + - '.github/workflows/create-infra-pipeline.yaml' - 'terraform/**/*' permissions: From 02b04b06573723d2b721901443c08562ccf5d37c Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 18:45:07 +0200 Subject: [PATCH 44/47] docs: update docs --- README.md | 40 +++++++++++++++++++------------ terraform/backend.tf | 3 +-- terraform/environments/int.tfvars | 0 terraform/environments/prd.tfvars | 0 4 files changed, 26 insertions(+), 17 deletions(-) delete mode 100644 terraform/environments/int.tfvars delete mode 100644 terraform/environments/prd.tfvars diff --git a/README.md b/README.md index 58fc78f..ebd3a01 100644 --- a/README.md +++ b/README.md @@ -2,24 +2,34 @@ A learning experience for setting up a CI/CD pipeline for Python ## ToDo -- Create Terraform scripts to deploy Docker image on Azure -- Create pipeline to setup infrastructure using Terraform scripts - Create (separate?) pipeline to run continuous delivery on newly setup infrastructure - Check remaining TODO comments - Add project documentation ## Infrastructure -Source: https://learn.microsoft.com/en-us/azure/container-instances/container-instances-quickstart-terraform -- Created managed identity `infra-user-dev` -- Use Federated Identity Credentials to enable OIDC and avoid managing (and rotating) secrets -- Problem: Azure needs exact branch path for credential (no wildcard) - - Seems to be by design: https://learn.microsoft.com/en-us/answers/questions/2073829/azure-github-action-federated-identity-login-issue -- TODO: Research how OIDC works exactly -- TODO: Explain that I will not separate environments here, though for real-life it would be mandatory -- TODO: Main resource group already exists in terraform setup - -**How to manage Terraform state?** -- GitHub has no feature to store the Terraform state like GitLab -- I'll use an Azure Storage Account -- https://developer.hashicorp.com/terraform/language/backend/azurerm#azure-active-directory +*In a real-world use case, one would seperate at least three environments of infrastructure: development, staging and production. This separation was out of the scope of this sample project. However, I showed how a possible separation could look like, e.g. with different .tfvar files.* + +TODO: Infrastructure Sketch + +**Requirements** + +For simplicity, I assumed that some resources are already present and did not automate the creation of them. I decided that this was out of scope of this sample project. Here are the (manual) steps you need to take in order to setup the project in Azure. This can be done either in the Azure Portal or via the Azure CLI: + +- Create a resource group (To contain all Azure resources associated with this repository) +- Create a storage account and container inside this resource group (To store Terraform state file) +- Create a user-managed identity +- Give the user-managed identity the following privileges: + - `Storage Blob Contributor` on the Storage Account Container (To read and write the Terraform state file) + - `Contributor` on the resource group (To add, change, and delete resources in it via Terraform) +- Create a federated credential for the main branch of your forked repository + - E.g. I created one with the following subject for the original repository: `repo:matrop/python-cicd:ref:refs/heads/main` + +**Open ID Connect** + +We use Open ID Connect (OIDC) in order to authenticate Terraform to Azure and provision resources. The usage of OIDC instead of client-secret-based authentication has several advantages: +- We do not need to store credentials in Azure or the CI/CD pipelines. They are requested automatically +- Credentials are short-lived and we do not need to rotate them +- Credentials are granular and enable a detailed access concept + +One downside with this is the lack of wildcards in the subject identifier, e.g. for branch names. I read about it [here](https://learn.microsoft.com/en-us/answers/questions/2073829/azure-github-action-federated-identity-login-issue). In a real-world use case where many different tags or branches, e.g. feature branches, would make and apply infrastructure changes, this would be an issue. diff --git a/terraform/backend.tf b/terraform/backend.tf index c497df6..b0fec71 100644 --- a/terraform/backend.tf +++ b/terraform/backend.tf @@ -1,9 +1,8 @@ terraform { - # TODO: Environment agnostic! backend "azurerm" { use_oidc = true use_azuread_auth = true - storage_account_name = "samauriceatropsdev" # TODO: How to account for different environments? + storage_account_name = "samauriceatropsdev" # This would need to be environment-specific container_name = "tfstate" key = "terraform.tfstate" } diff --git a/terraform/environments/int.tfvars b/terraform/environments/int.tfvars deleted file mode 100644 index e69de29..0000000 diff --git a/terraform/environments/prd.tfvars b/terraform/environments/prd.tfvars deleted file mode 100644 index e69de29..0000000 From 133e2b46919887510274e86102420c84bda8c587 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 18:57:33 +0200 Subject: [PATCH 45/47] docs: update docs 2 --- .github/workflows/create-infra-pipeline.yaml | 1 + README.md | 26 ++------------------ 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/.github/workflows/create-infra-pipeline.yaml b/.github/workflows/create-infra-pipeline.yaml index 57c22c3..633133d 100644 --- a/.github/workflows/create-infra-pipeline.yaml +++ b/.github/workflows/create-infra-pipeline.yaml @@ -20,6 +20,7 @@ jobs: run-tf: name: "Run Terraform Code" runs-on: ubuntu-latest + environment: development # This would need to be environment-specific defaults: run: working-directory: ./terraform diff --git a/README.md b/README.md index ebd3a01..cea8784 100644 --- a/README.md +++ b/README.md @@ -6,30 +6,8 @@ A learning experience for setting up a CI/CD pipeline for Python - Check remaining TODO comments - Add project documentation -## Infrastructure - -*In a real-world use case, one would seperate at least three environments of infrastructure: development, staging and production. This separation was out of the scope of this sample project. However, I showed how a possible separation could look like, e.g. with different .tfvar files.* +## Infrastructure TODO: Infrastructure Sketch -**Requirements** - -For simplicity, I assumed that some resources are already present and did not automate the creation of them. I decided that this was out of scope of this sample project. Here are the (manual) steps you need to take in order to setup the project in Azure. This can be done either in the Azure Portal or via the Azure CLI: - -- Create a resource group (To contain all Azure resources associated with this repository) -- Create a storage account and container inside this resource group (To store Terraform state file) -- Create a user-managed identity -- Give the user-managed identity the following privileges: - - `Storage Blob Contributor` on the Storage Account Container (To read and write the Terraform state file) - - `Contributor` on the resource group (To add, change, and delete resources in it via Terraform) -- Create a federated credential for the main branch of your forked repository - - E.g. I created one with the following subject for the original repository: `repo:matrop/python-cicd:ref:refs/heads/main` - -**Open ID Connect** - -We use Open ID Connect (OIDC) in order to authenticate Terraform to Azure and provision resources. The usage of OIDC instead of client-secret-based authentication has several advantages: -- We do not need to store credentials in Azure or the CI/CD pipelines. They are requested automatically -- Credentials are short-lived and we do not need to rotate them -- Credentials are granular and enable a detailed access concept - -One downside with this is the lack of wildcards in the subject identifier, e.g. for branch names. I read about it [here](https://learn.microsoft.com/en-us/answers/questions/2073829/azure-github-action-federated-identity-login-issue). In a real-world use case where many different tags or branches, e.g. feature branches, would make and apply infrastructure changes, this would be an issue. +For more detailed info see [Infrastructure README](terraform/README.md). From 2e08d56af9f3698b92543da10334a15f690339b3 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 18:59:15 +0200 Subject: [PATCH 46/47] docs: separate infra docs --- terraform/README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 terraform/README.md diff --git a/terraform/README.md b/terraform/README.md new file mode 100644 index 0000000..0421ef5 --- /dev/null +++ b/terraform/README.md @@ -0,0 +1,35 @@ +## Infrastructure + +*In a real-world use case, one would seperate at least three environments of infrastructure: development, staging and production. This separation was out of the scope of this sample project. However, I showed how a possible separation could look like, e.g. with different .tfvar files.* + +**Requirements** + +For simplicity, I assumed that some resources are already present and did not automate the creation of them. I decided that this was out of scope of this sample project. Here are the (manual) steps you need to take in order to setup the project in Azure. This can be done either in the Azure Portal or via the Azure CLI: + +- Create Accounts + - Create a DockerHub Account and an access token for it + - Create an Azure Account +- Create Resources + - Create a resource group (To contain all Azure resources associated with this repository) + - Create a storage account and container inside this resource group (To store Terraform state file) + - Create a user-managed identity +- Configure Resources + - Give the user-managed identity the following privileges: + - `Storage Blob Contributor` on the Storage Account Container (To read and write the Terraform state file) + - `Contributor` on the resource group (To add, change, and delete resources in it via Terraform) + - Create a federated credential for the main branch of your forked repository + - E.g. I created one with the following subject for the original repository: `repo:matrop/python-cicd:ref:refs/heads/main` +- Add GitHub Actions Secrets + - Add the client id of the newly created identity as GitHub Actions secret `INFRA_ARM_CLIENT_ID` + - Add the tenant id of your Azure account as GitHub Actions secret `ARM_TENANT_ID` + - Add the DockerHub Account name as GitHub Actions secret `DOCKERHUB_USERNAME` + - Add the DockerHub Account token as GitHub Actions secret `DOCKERHUB_TOKEN` + +**Open ID Connect** + +We use Open ID Connect (OIDC) in order to authenticate Terraform to Azure and provision resources. The usage of OIDC instead of client-secret-based authentication has several advantages: +- We do not need to store credentials in Azure or the CI/CD pipelines. They are requested automatically +- Credentials are short-lived and we do not need to rotate them +- Credentials are granular and enable a detailed access concept + +One downside with this is the lack of wildcards in the subject identifier, e.g. for branch names. I read about it [here](https://learn.microsoft.com/en-us/answers/questions/2073829/azure-github-action-federated-identity-login-issue). In a real-world use case where many different tags or branches, e.g. feature branches, would make and apply infrastructure changes, this would be an issue. \ No newline at end of file From a1362401149c2e2a7570adf032fd9541910006b0 Mon Sep 17 00:00:00 2001 From: Maurice Atrops Date: Sun, 18 May 2025 19:00:32 +0200 Subject: [PATCH 47/47] fix: more detailed file paths --- .github/workflows/ci-pipeline.yaml | 3 ++- .github/workflows/create-infra-pipeline.yaml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-pipeline.yaml b/.github/workflows/ci-pipeline.yaml index f39c3c8..e459ad8 100644 --- a/.github/workflows/ci-pipeline.yaml +++ b/.github/workflows/ci-pipeline.yaml @@ -9,7 +9,8 @@ on: - '.github/workflows/ci-pipeline.yaml' - 'src/**/*' - 'tests/**/*' - - 'terraform/**/*' + - 'terraform/**/*.tf' + - 'terraform/**/*.tfvars' - 'Dockerfile' - 'pyproject.toml' diff --git a/.github/workflows/create-infra-pipeline.yaml b/.github/workflows/create-infra-pipeline.yaml index 633133d..f769978 100644 --- a/.github/workflows/create-infra-pipeline.yaml +++ b/.github/workflows/create-infra-pipeline.yaml @@ -7,7 +7,8 @@ on: - 'feature/**' paths: - '.github/workflows/create-infra-pipeline.yaml' - - 'terraform/**/*' + - 'terraform/**/*.tf' + - 'terraform/**/*.tfvars' permissions: id-token: write # Needed for Azure CLI Login