Adding TF bootstrap for Gitlab and corresponding CICD templates (#409)

* Adding TF bootstrap for Gitlab and corresponding CICD templates

* Remove reference to old project

* Addressing PR feedback by @TechnicallyWilliams

* Apply suggestions from code review

Incorporating PR feedback from @TechnicallyWilliams

Co-authored-by: Dexter Williams <dexterwilliams04@gmail.com>

* PR Feedback

Co-authored-by: Dexter Williams <dexterwilliams04@gmail.com>
This commit is contained in:
Nicholas M. Iodice 2020-09-22 14:28:36 -04:00 коммит произвёл GitHub
Родитель df6eaf803d
Коммит 4f5acaf005
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
31 изменённых файлов: 1010 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euox pipefail
az login --service-principal -u "$ARM_CLIENT_ID" -p "$ARM_CLIENT_SECRET" --tenant "$ARM_TENANT_ID"

Просмотреть файл

@ -0,0 +1,34 @@
# This file contains the variables and jobs for the dev stage
.dev-vars: &dev-vars
ARM_CLIENT_ID: $DEV_ARM_CLIENT_ID
ARM_CLIENT_SECRET: $DEV_ARM_CLIENT_SECRET
AZURE_STORAGE_ACCOUNT_NAME: $DEV_AZURE_STORAGE_ACCOUNT_NAME
AZURE_STORAGE_ACCOUNT_CONTAINER: $DEV_AZURE_STORAGE_ACCOUNT_CONTAINER
AZURE_STORAGE_ACCOUNT_SUBSCRIPTION: $DEV_AZURE_STORAGE_ACCOUNT_SUBSCRIPTION
VAR_FILE_NAME: 'DEV_TF_VARS'
ENVIRONMENT: 'dev'
DevBuild:
extends: .build
stage: Dev-Build
variables:
<<: *dev-vars
only:
- master
- merge_requests
- web
DevRelease:
extends: .release
stage: Dev-Release
variables:
<<: *dev-vars
needs:
- job: DevBuild
artifacts: true
when: on_success
only:
- master
- merge_requests
- web

Просмотреть файл

@ -0,0 +1,34 @@
# This file contains the variables and jobs for the integration stage
.integration-vars: &integration-vars
ARM_CLIENT_ID: $INTEGRATION_ARM_CLIENT_ID
ARM_CLIENT_SECRET: $INTEGRATION_ARM_CLIENT_SECRET
AZURE_STORAGE_ACCOUNT_NAME: $INTEGRATION_AZURE_STORAGE_ACCOUNT_NAME
AZURE_STORAGE_ACCOUNT_CONTAINER: $INTEGRATION_AZURE_STORAGE_ACCOUNT_CONTAINER
AZURE_STORAGE_ACCOUNT_SUBSCRIPTION: $INTEGRATION_AZURE_STORAGE_ACCOUNT_SUBSCRIPTION
VAR_FILE_NAME: 'INTEGRATION_TF_VARS'
ENVIRONMENT: 'integration'
IntegrationBuild:
extends: .build
stage: Integration-Build
variables:
<<: *integration-vars
only:
- master
needs:
- job: DevRelease
artifacts: false
IntegrationRelease:
extends: .release
stage: Integration-Release
variables:
<<: *integration-vars
needs:
- job: IntegrationBuild
artifacts: true
when: manual
allow_failure: false
only:
- master

Просмотреть файл

@ -0,0 +1,43 @@
# This file contains all of the checks that need to happen
# before a build and release.
# Cache dependent modules + proviers and upload an artifact. We need to
# make sure that the backend configuration is not configured, since it will
# need to be done for each stage. The configuration per stage may differ, so
# it makes sense to defer the initialization.
InitTF:
stage: Init
script: .ci/tf-init-without-backend.sh
artifacts:
paths:
- .terraform/
only:
- master
- merge_requests
# Lint check terraform files
LintTF:
stage: Pre-Build
script: .ci/tf-lint.sh
only:
- master
- merge_requests
# Validate terraform configuration
ValidateTF:
stage: Pre-Build
script: .ci/tf-validate.sh
needs:
- job: InitTF
artifacts: true
only:
- master
- merge_requests
# Lint check go files
lintGo:
stage: Pre-Build
script: .ci/go-lint.sh
only:
- master
- merge_requests

Просмотреть файл

@ -0,0 +1,34 @@
# This file contains the variables and jobs for the prod stage
.prod-vars: &prod-vars
ARM_CLIENT_ID: $PROD_ARM_CLIENT_ID
ARM_CLIENT_SECRET: $PROD_ARM_CLIENT_SECRET
AZURE_STORAGE_ACCOUNT_NAME: $PROD_AZURE_STORAGE_ACCOUNT_NAME
AZURE_STORAGE_ACCOUNT_CONTAINER: $PROD_AZURE_STORAGE_ACCOUNT_CONTAINER
AZURE_STORAGE_ACCOUNT_SUBSCRIPTION: $PROD_AZURE_STORAGE_ACCOUNT_SUBSCRIPTION
VAR_FILE_NAME: 'PROD_TF_VARS'
ENVIRONMENT: 'prod'
ProdBuild:
extends: .build
stage: Prod-Build
variables:
<<: *prod-vars
only:
- master
needs:
- job: IntegrationRelease
artifacts: false
ProdRelease:
extends: .release
stage: Prod-Release
variables:
<<: *prod-vars
needs:
- job: ProdBuild
artifacts: true
when: manual
allow_failure: false
only:
- master

Просмотреть файл

@ -0,0 +1,27 @@
# This file contains jobs that can be extended by stage-specific
# build and release steps
# Generic terraform build job
.build:
environment:
name: $ENVIRONMENT
artifacts:
paths:
- .terraform/
- "$PLAN_FILE"
script:
- .ci/az-login.sh
- .ci/tf-init-for-stage.sh
- .ci/tf-workspace-select.sh
- .ci/tf-plan.sh
# Generic terraform release job
.release:
environment:
name: $ENVIRONMENT
allow_failure: false
script:
- .ci/az-login.sh
- .ci/tf-init-for-stage.sh
- .ci/tf-workspace-select.sh
- .ci/tf-apply.sh

Просмотреть файл

@ -0,0 +1,19 @@
#!/usr/bin/env bash
# Note: the omission of the `pipefail` flag is intentional. It allows this
# step to succeede in the case that there are no `*.go` files in the
# infrastructure repository.
set -euox
echo "Linting Go Files... If this fails, run 'go fmt ./...' to fix"
# This runs a go fmt on each file without using the 'go fmt ./...' syntax.
# This is advantageous because it avoids having to download all of the go
# dependencies that would have been triggered by using the './...' syntax.
FILES_WITH_FMT_ISSUES=$(find . -name "*.go" | grep -v '.terraform' | xargs gofmt -l | wc -l)
# convert to integer...
FILES_WITH_FMT_ISSUES=$(($FILES_WITH_FMT_ISSUES + 0))
# set exit code accordingly
exit $FILES_WITH_FMT_ISSUES

Просмотреть файл

@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euox pipefail
terraform apply -input=false -auto-approve $PLAN_FILE

Просмотреть файл

@ -0,0 +1,4 @@
# #!/usr/bin/env bash
set -euox pipefail
terraform init -backend-config "storage_account_name=$AZURE_STORAGE_ACCOUNT_NAME" -backend-config "container_name=$AZURE_STORAGE_ACCOUNT_CONTAINER"

Просмотреть файл

@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euox pipefail
terraform init -backend=false

Просмотреть файл

@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euox pipefail
echo "Linting Terraform Files... If this fails, run 'terraform fmt -recursive' to fix"
terraform fmt -recursive -check

Просмотреть файл

@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euox pipefail
# workaround for https://gitlab.com/gitlab-org/gitlab-foss/-/issues/65763
terraform plan -var-file="${!VAR_FILE_NAME}" -out "$PLAN_FILE"

Просмотреть файл

@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euox pipefail
terraform validate

Просмотреть файл

@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euox pipefail
terraform workspace new $ENVIRONMENT || terraform workspace select $ENVIRONMENT

Просмотреть файл

@ -0,0 +1,40 @@
# Extension points not yet implemented:
# (1) Run infrastructure unit tests in precheck stage
# (2) Run infrastructure integration tests
# Configure the base image for CI jobs here.
#
# To override the entrypoint see the documentation here:
# https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#overriding-the-entrypoint-of-an-image
#
# To use a custom image, use the following syntax:
# name: "$CI_REGISTRY/<your image here>:<your tag here>"
image:
name: "{{IMAGE_SLUG}}"
entrypoint:
- '/usr/bin/env'
- 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
include:
- '/.ci/ci-stages.yml'
- '/.ci/ci-prechecks.yml'
- '/.ci/ci-dev.yml'
- '/.ci/ci-integration.yml'
- '/.ci/ci-prod.yml'
before_script:
- terraform version
stages:
- Init
- Pre-Build
- Dev-Build
- Dev-Release
- Integration-Build
- Integration-Release
- Prod-Build
- Prod-Release
variables:
TF_IN_AUTOMATION: "true"
PLAN_FILE: tfplan.out

Просмотреть файл

@ -0,0 +1,13 @@
FROM hashicorp/terraform:0.12.29
# Add common dependencies
RUN apk update && \
apk add openssl curl tar gzip bash ca-certificates coreutils
# Add Azure CLI
RUN \
apk add py3-pip && \
apk add --virtual=build gcc libffi-dev musl-dev openssl-dev python3-dev make && \
pip3 --no-cache-dir install -U pip && \
pip3 --no-cache-dir install azure-cli && \
apk del --purge build

Просмотреть файл

@ -0,0 +1,72 @@
# Terraform Deployments on Gitlab
This repository contains CICD templates that can deploy [Terraform](https://www.terraform.io/) templates for production systems into Azure using Gitlab.
## Automated CICD
Deployment of Terraform Templates are fully automated via the [GitLab Pipeline](./.gitlab-ci.yml).
**Terminology**
* `init` causes Terraform modules to be downloaded/cached
* `lint` applies lint checks to the IAC templates and any Go files
* `build` is a Terraform plan
* `release` is a Terraform apply
The table below outlines when a deployment will happen to each stage:
> **Note**: Manual approvals are **always required** before deploying to non-`dev` environments
| Action | Pipeline Stages (sequential) |
| --- | --- |
| Manual branch build | `dev::build`, `dev::release` |
| Create/Update PR | `init`, `lint`, `dev::build`, `dev::release` |
| Merge PR | `init`, `lint`, `dev::build`, `dev::release`, `integration::build`, `integration::release`, `prod::build`, `prod::release` |
| Master Branch Build | Same as above |
## Infrastructure Rollbacks
It is possible that Terraform deployments will need to be rolled back. To rollback use `git revert` commands or simply make another commit to return your configuration to a previous state in the infrastructure as code repository.
Be sure to create a local git branch, then commit, push, and generate a pull request on GitLab.
## Usage
**Step 1: Azure & Gitlab Configuration**
All Azure and Gitlab configuration required to use these templates should be provisioned using the [`gitlab-bootstrap-iac-cicd`](../../../../infra/templates/gitlab-bootstrap-iac-cicd/README.md) template. No other manual configuration is necessary.
**Step 2: Build and Push Gitlab Runner Base Image**
The CICD templates in this folder assume the following tools are installed.
* Terraform v0.12.x
* Golang
* Azure CLI
The included [`Dockerfile.sample`](Dockerfile.sample) can be used as a starting point. Use these commands to push the base image to the ACR:
```bash
# Configure environment
$ ACR_NAME="..."
$ IMAGE="..."
$ TAG="latest"
# Build and push image
$ az acr login -n "$ACR_NAME"
$ docker build -f Dockerfile.sample . -t "$ACR_NAME.azurecr.io/$IMAGE:$TAG"
$ docker push "$ACR_NAME.azurecr.io/$IMAGE:$TAG"
```
**Step 3: Configure Gitlab Pipeline to Use Base Image**
Now insert a custom value for the base image reference/property in `.gitlab-ci.yml`:
> **Note**: The use of `\$CI_REGISTRY` in the command below is intentional. When this pipeline is exercised, the value of `CI_REGISTRY` will be resolved because the [`gitlab-bootstrap-iac-cicd`](../../../../infra/templates/gitlab-bootstrap-iac-cicd/) template from step 1 configured it to point to the correct container registry.
```bash
$ sed -i '' "s/{{IMAGE_SLUG}}/\$CI_REGISTRY\/$IMAGE:$TAG/g" .gitlab-ci.yml
```
**Step 4: Write Some Terraform!**
At this point, you can begin writing a Terraform template for your deployment. The [`sample.tf`](./sample.tf) file is a sample Terraform template that shows a simple but working Terraform file that uses the backend state and variables configured through the Gitlab/Azure bootstrapping process referenced above.

Просмотреть файл

@ -0,0 +1,44 @@
# This file in intended to show how to leverage the following, which are provisioned
# through the Azure/Gitlab bootstrapping process.
# (1) Backend State
# (2) TF Vars
#
# See README.me for more
provider "azurerm" {
version = "=2.22"
features {}
}
terraform {
backend "azurerm" {
key = "terraform.tfstate"
}
}
variable "env" {
type = string
description = "The name of the environment to provision. Examples: dev, qa, prod"
}
variable "resource_group" {
type = string
description = "The resource group to deploy into"
}
variable "acr_id" {
type = string
description = "The resource identifier for AKS to attach to"
}
output "echo_env" {
value = var.env
}
output "echo_resource_group" {
value = var.resource_group
}
output "echo_acr_id" {
value = var.acr_id
}

Просмотреть файл

@ -0,0 +1,10 @@
export GITLAB_TOKEN="..."
export TF_VAR_group_path="..."
export TF_VAR_gitlab_terraform_project_path="..."
export TF_VAR_location="..."
export TF_VAR_prefix="..."
# Backend state configuration. Uncomment after configuring backend state.
# export ARM_ACCESS_KEY="..."
# export ARM_ACCOUNT_NAME="..."
# export ARM_CONTAINER_NAME="..."

34
infra/templates/gitlab-bootstrap-iac-cicd/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,34 @@
# Created by https://www.toptal.com/developers/gitignore/api/terraform
# Edit at https://www.toptal.com/developers/gitignore?templates=terraform
### Terraform ###
# Local .terraform directories
**/.terraform/*
# .tfstate files
*.tfstate
*.tfstate.*
# Crash log files
crash.log
# Ignore any .tfvars files that are generated automatically for each Terraform run. Most
# .tfvars files are managed as part of configuration and so should be included in
# version control.
#
# example.tfvars
# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Include override files you do wish to add to version control using negated pattern
# !example_override.tf
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*
# End of https://www.toptal.com/developers/gitignore/api/terraform

Просмотреть файл

@ -0,0 +1,200 @@
# Bootstrap for Terraform deployments through Gitlab into Azure
This directory contains [Terraform](https://www.terraform.io/) templates that can bootstrap Azure and Gitlab resources in a way that enables running robust CICD of [Terraform](https://www.terraform.io/) templates using [Gitlab CICD](https://docs.gitlab.com/ee/ci/). After applying this template, automated CI of terraform deployments should **just work**.
> **Note**: This template is intended to be used alongside the CICD pipeline for infrastructure using Gitlab. More information can be found [here](../../../devops/providers/gitlab/templates/README.md)
At a high level, this template aims to:
* Deploy Azure Dependencies required for automated CICD of Terraform deployments
* Configure variables in GitLab required for automated CICD of Terraform deployments
* Configure dependencies for each a multistage (`dev`, `integration`, `prod`, etc...) Terraform deployment
> **Note**: This template only sets up the **dependencies** needed to do a production ready infrastructure deployment, such as backend state, deployment credentials, Azure Contianer Reigstry and Gitlab variables.
There are many things deployed by this template, including:
* Backend state storage account
* Backend state containers for this deployment
* ACR for storing docker images
* GitLab variables needed for all deployments
* For each deployment environment
* Backend state container
* Service principal used for deployments to that environment
* Resource group
* Role based security
## Identities/Credentials Configured
This template will generate some credentials, which are enumarated blow:
| Description | Reason | Notes |
| --- | --- | --- |
| ACR Push/Pull | Needed by the pipeline that builds the base image used by all of the infrastructure CICD in GitLab | N/A |
| Environment Deploy | Needed by each environment to execute a deployment of resources into Azure | One generated for each environment |
## Usage
There are a few use cases for the code in this repository. The sections below outline the usage for each of those cases
### First Time Setup
Among the many resources provisioned by this template is the [Backend Configuration](https://www.terraform.io/docs/backends/index.html) that hosts the [Terraform State](https://www.terraform.io/docs/state/index.html) for this template, as well as the state for each deployment.
Because of this, we cannot have the backend state configured for the initial deployment of this template. These steps will take you through the following:
* Initial deployment of this template
* Enable the backend state for this deployment
#### Requirements
* `terraform` will need to be installed. Version `v0.12.28` or newer is recommended
* A shell environment, preferrably bash
* A Gitlab personall access token. Instructions for generating one can be found [here](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html). The token will need the `api` permission.
* An Azure subscription
#### Deployment Steps
**Disable backend state**
For the first deployment, the contents of `backend.tf` will need to be commented out. Don't worry -- we'll uncomment this later.
**Configure your environment**
```bash
# Required to configure variables in GitLab
export GITLAB_TOKEN="..."
# Required to scope variables to the GitLab group
export TF_VAR_group_path="..."
# Required to scope variables to the GitLab project that houses Terraform. This
# should be in the form of $GROUP/$PROJECT_NAME
export TF_VAR_gitlab_terraform_project_path="..."
# The location in which to provision Azure resources
export TF_VAR_location="..."
# The prefix used for naming resources in Azure
export TF_VAR_prefix="..."
# Log into the Azure CLI
az login
# Set your default subscription - this will dictate where resources will be provisioned
az account set --subscription "<your subscription ID>"
```
**Run the deployment**
> **Note**: If you see a log about `Initializing the backend...`, make sure that you followed the steps to disable the backend state.
```bash
# Initialize the Terraform environment
terraform init
# See what the deployment will do. No changes will be applied, but you can review the changes that will be applied in the next step
terraform plan
# Deploy the changes
terraform apply
```
**Enable backend state**
Enabling backend state will store the deployment state in Azure. This will allow others to run the deployment without you needing to worry about the state configuration.
Start by uncommenting the contents of `backend.tf`, then run the following:
```bash
export ARM_ACCESS_KEY=$(terraform output backend-state-account-key)
export ARM_ACCOUNT_NAME=$(terraform output backend-state-account-name)
export ARM_CONTAINER_NAME=$(terraform output backend-state-bootstrap-container-name)
# Initialize the deployment with the backend
terraform init -backend-config "storage_account_name=${ARM_ACCOUNT_NAME}" -backend-config "container_name=${ARM_CONTAINER_NAME}"
```
You should see something along the lines of the following, to which you want to answer `yes`:
```bash
Do you want to copy existing state to the new backend?
```
If things work, you will see the following message and the state file should end up in Azure:
```bash
Successfully configured the backend "azurerm"! Terraform will automatically
use this backend unless the backend configuration changes.
```
### Deploying the Infrastructure
Now that Azure and GitLab have been configured to support the Terraform deployment, you will need to do the following to actually deploy the environment.
**Trigger IAC Pipeline**
You are now ready to kick off a deployment of the IAC pipeline! You can do this through the GitLab UI.
### Rotate Service Principal Passwords
If the need arises to rotate the credentials for any of the generated service principals, the following command can be used to quickly rotate the credentials and also update all configuration in GitLab:
```bash
# configure environment (.envrc.template)
az login
az account set --subscription "<your subscription ID>"
terraform init -backend-config "storage_account_name=${ARM_ACCOUNT_NAME}" -backend-config "container_name=${ARM_CONTAINER_NAME}"
# `taint` all passwords - this triggers Terraform to regenerate these and update all dependent configuration
terraform state list | grep random_password | xargs -L 1 terraform taint
terraform plan
# Note: this command might fail due to the rapid create/delete on Azure resources. If it fails, re-running it
# should solve the issue
terraform apply
```
Done!
### Adding a new environment
Now that Azure and GitLab have been configured to deploy resources through Terraform, you can easily configure Azure and GitLab to support new application stages (environments) by using the `environment` module.
> **Note**: This will only set up Azure and GitLab to support a new environment. The environment will need to be deployed using the infrastructure deployments project (not covered here).
This guide will take you through configuring Azure and GitLab to support a new `pre-prod` environment.
**Add a new environment**
You will need to open `azure.tf` to configure a new environment. A new environment can be configured by adding the following to the bottom of the file:
```hcl
module "preprod" {
source = "./environment"
acr_id = azurerm_container_registry.acr.id
environment_name = "preprod"
location = var.location
subscription_id = data.azurerm_client_config.current.subscription_id
backend_storage_account_name = azurerm_storage_account.ci.name
gitlab_terraform_project_path = var.gitlab_terraform_project_path
prefix = var.prefix
}
```
You will then need to execute the following:
```bash
# configure environment (.envrc.template)
az login
az account set --subscription "<your subscription ID>"
terraform init -backend-config "storage_account_name=${ARM_ACCOUNT_NAME}" -backend-config "container_name=${ARM_CONTAINER_NAME}"
terraform plan
terraform apply
```
Done!

Просмотреть файл

@ -0,0 +1,39 @@
resource "azurerm_container_registry" "acr" {
name = format("acr%s", random_string.rand.result)
resource_group_name = azurerm_resource_group.ci.name
location = azurerm_resource_group.ci.location
sku = "Basic"
}
resource "azuread_application" "acr" {
name = format("acr-push-%s", random_string.rand.result)
}
resource "azuread_service_principal" "acr" {
application_id = azuread_application.acr.application_id
}
resource "random_password" "acr" {
length = 35
upper = true
lower = true
special = false
}
resource "azuread_service_principal_password" "acr" {
service_principal_id = azuread_service_principal.acr.id
value = random_password.acr.result
end_date_relative = "2400h"
}
resource "azurerm_role_assignment" "acr_push" {
scope = azurerm_container_registry.acr.id
role_definition_name = "AcrPush"
principal_id = azuread_service_principal.acr.id
}
resource "azurerm_role_assignment" "acr_pull" {
scope = azurerm_container_registry.acr.id
role_definition_name = "AcrPull"
principal_id = azuread_service_principal.acr.id
}

Просмотреть файл

@ -0,0 +1,62 @@
resource "random_string" "rand" {
length = 4
special = false
number = false
upper = false
}
resource "azurerm_resource_group" "ci" {
name = format("rg-%s-ci", var.prefix)
location = var.location
}
resource "azurerm_storage_account" "ci" {
name = format("backendstate%s", random_string.rand.result)
resource_group_name = azurerm_resource_group.ci.name
location = azurerm_resource_group.ci.location
min_tls_version = "TLS1_2"
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_storage_container" "tfstate" {
name = "tfstate-terraform-bootstrap"
storage_account_name = azurerm_storage_account.ci.name
container_access_type = "private"
}
data "azurerm_client_config" "current" {}
module "dev" {
source = "./environment"
acr_id = azurerm_container_registry.acr.id
environment_name = "dev"
location = var.location
subscription_id = data.azurerm_client_config.current.subscription_id
backend_storage_account_name = azurerm_storage_account.ci.name
gitlab_terraform_project_path = var.gitlab_terraform_project_path
prefix = var.prefix
}
module "integration" {
source = "./environment"
acr_id = azurerm_container_registry.acr.id
environment_name = "integration"
location = var.location
subscription_id = data.azurerm_client_config.current.subscription_id
backend_storage_account_name = azurerm_storage_account.ci.name
gitlab_terraform_project_path = var.gitlab_terraform_project_path
prefix = var.prefix
}
module "prod" {
source = "./environment"
acr_id = azurerm_container_registry.acr.id
environment_name = "prod"
location = var.location
subscription_id = data.azurerm_client_config.current.subscription_id
backend_storage_account_name = azurerm_storage_account.ci.name
gitlab_terraform_project_path = var.gitlab_terraform_project_path
prefix = var.prefix
}

Просмотреть файл

@ -0,0 +1,5 @@
terraform {
backend "azurerm" {
key = "tf-bootstrap.tfstate"
}
}

Просмотреть файл

@ -0,0 +1,44 @@
locals {
full_name = format("%s-%s", var.prefix, var.environment_name)
}
resource "azurerm_resource_group" "rg" {
location = var.location
name = "rg-${local.full_name}"
tags = {
environment = var.environment_name
}
}
resource "azuread_application" "app" {
name = "sp-${local.full_name}"
}
resource "azuread_service_principal" "sp" {
application_id = azuread_application.app.application_id
}
resource "random_password" "sp" {
length = 35
upper = true
lower = true
special = false
}
resource "azuread_service_principal_password" "sp" {
service_principal_id = azuread_service_principal.sp.id
value = random_password.sp.result
end_date_relative = "2400h"
}
resource "azurerm_role_assignment" "rg-owner" {
scope = azurerm_resource_group.rg.id
role_definition_name = "Owner"
principal_id = azuread_service_principal.sp.id
}
resource "azurerm_storage_container" "tfstate" {
name = format("tfstate-%s", var.environment_name)
storage_account_name = var.backend_storage_account_name
container_access_type = "private"
}

Просмотреть файл

@ -0,0 +1,62 @@
locals {
tf_vars_file = <<EOF
env="${var.environment_name}"
resource_group="${azurerm_resource_group.rg.name}"
acr_id="${var.acr_id}"
EOF
}
resource "gitlab_project_variable" "sp_client_id" {
project = var.gitlab_terraform_project_path
key = format("%s_ARM_CLIENT_ID", upper(var.environment_name))
value = azuread_service_principal.sp.application_id
protected = false
masked = true
environment_scope = var.environment_name
}
resource "gitlab_project_variable" "sp_client_secret" {
project = var.gitlab_terraform_project_path
key = format("%s_ARM_CLIENT_SECRET", upper(var.environment_name))
value = random_password.sp.result
protected = false
masked = true
environment_scope = var.environment_name
}
resource "gitlab_project_variable" "tf_vars_file" {
project = var.gitlab_terraform_project_path
key = format("%s_TF_VARS", upper(var.environment_name))
value = local.tf_vars_file
variable_type = "file"
protected = false
environment_scope = var.environment_name
}
resource "gitlab_project_variable" "storage_account" {
project = var.gitlab_terraform_project_path
key = format("%s_AZURE_STORAGE_ACCOUNT_NAME", upper(var.environment_name))
value = var.backend_storage_account_name
protected = false
masked = true
environment_scope = var.environment_name
}
resource "gitlab_project_variable" "storage_container" {
project = var.gitlab_terraform_project_path
key = format("%s_AZURE_STORAGE_ACCOUNT_CONTAINER", upper(var.environment_name))
value = azurerm_storage_container.tfstate.name
protected = false
masked = true
environment_scope = var.environment_name
}
resource "gitlab_project_variable" "storage_subscription" {
project = var.gitlab_terraform_project_path
key = format("%s_AZURE_STORAGE_ACCOUNT_SUBSCRIPTION", upper(var.environment_name))
value = var.subscription_id
protected = false
masked = true
environment_scope = var.environment_name
}

Просмотреть файл

@ -0,0 +1,35 @@
variable "environment_name" {
type = string
description = "The name of the environment"
}
variable "location" {
type = string
description = "The region to deploy the environment to"
}
variable "subscription_id" {
type = string
description = "The subscription id to create service principals in"
}
variable "acr_id" {
type = string
description = "ACR ID to use for kubernetes deployments"
}
variable "backend_storage_account_name" {
type = string
description = "the name of the storage account in which to provision a tf state container"
}
variable "gitlab_terraform_project_path" {
type = string
description = "Path of project in gitlab that houses infrastructure templates"
}
variable "prefix" {
type = string
description = "Naming prefix for resources in Azure"
}

Просмотреть файл

@ -0,0 +1,74 @@
locals {
docker_auth = base64encode(join(":", [azuread_service_principal.acr.application_id, random_password.acr.result]))
docker_auth_json = <<EOF
{
"auths": {
"${azurerm_container_registry.acr.login_server}": {
"auth": "${local.docker_auth}"
}
}
}
EOF
}
data "gitlab_group" "group" {
full_path = var.group_path
}
resource "gitlab_group_variable" "registry" {
group = data.gitlab_group.group.id
key = "CI_REGISTRY"
value = azurerm_container_registry.acr.login_server
protected = false
masked = true
}
resource "gitlab_group_variable" "registry_user" {
group = data.gitlab_group.group.id
key = "CI_REGISTRY_USER"
value = azuread_service_principal.acr.application_id
protected = false
masked = true
}
resource "gitlab_group_variable" "registry_password" {
group = data.gitlab_group.group.id
key = "CI_REGISTRY_PASSWORD"
value = random_password.acr.result
protected = false
masked = true
}
resource "gitlab_group_variable" "docker_auth" {
group = data.gitlab_group.group.id
key = "DOCKER_AUTH_CONFIG"
value = local.docker_auth_json
protected = false
}
resource "gitlab_project_variable" "sub_id" {
project = var.gitlab_terraform_project_path
key = "ARM_SUBSCRIPTION_ID"
value = data.azurerm_client_config.current.subscription_id
protected = false
masked = true
environment_scope = "*"
}
resource "gitlab_project_variable" "tenant_id" {
project = var.gitlab_terraform_project_path
key = "ARM_TENANT_ID"
value = data.azurerm_client_config.current.tenant_id
protected = false
masked = true
environment_scope = "*"
}
resource "gitlab_project_variable" "storage_key" {
project = var.gitlab_terraform_project_path
key = "ARM_ACCESS_KEY"
value = azurerm_storage_account.ci.primary_access_key
protected = false
masked = true
environment_scope = "*"
}

Просмотреть файл

@ -0,0 +1,12 @@
output "backend-state-account-name" {
value = azurerm_storage_account.ci.name
}
output "backend-state-account-key" {
value = azurerm_storage_account.ci.primary_access_key
}
output "backend-state-bootstrap-container-name" {
value = azurerm_storage_container.tfstate.name
}

Просмотреть файл

@ -0,0 +1,20 @@
provider "azurerm" {
version = "=2.22"
features {}
}
provider "azuread" {
version = "=0.10.0"
}
provider "gitlab" {
version = "=2.10.0"
}
provider "random" {
version = "=2.2.1"
}
provider "tls" {
version = "=2.1.1"
}

Просмотреть файл

@ -0,0 +1,19 @@
variable "group_path" {
type = string
description = "The group that secrets should be provisioned into"
}
variable "location" {
type = string
description = "Location in which to provision Azure resources"
}
variable "gitlab_terraform_project_path" {
type = string
description = "Path of project in gitlab that houses infrastructure templates"
}
variable "prefix" {
type = string
description = "Naming prefix for resources in Azure"
}