-
Notifications
You must be signed in to change notification settings - Fork 2
Terraform Guide Azure
Terraform is an IaC tool used to create and change your virtual infrastructure. Custodian team uses Terraform for testing policies. Creation of a Terraform code will require you to follow the procedure and general rules introduced below.
-
Pre-requisites
1.1. Create folders
1.2 Green/Red folder structure -
Rules for Terraform code creation
2.1. Split terraform resources by service into separate files
2.2. Content of "provider.tf" file
2.3. Content of "variables.tf" file
2.4. Content of "terraform.tfvars" file
2.5. A "resource" block name
2.6. Value of the argument "Name"
2.7. Tags
2.9. Random password generation - Terraform language style conventions
- Terraform basic tutorials
Before you start writing a code, you need to create the necessary folders.
Perform the following steps:
1. In the local branch, in the folder "terraform", create a general folder with the name of the policy, e.g., "ecc-azure-000-example_policy"
.
2. In this folder, create three other folders:
- Folder with "green" infrastructure – name:
"green"
- Folder with a JSON file with permissions for a user to run Custodian policy – name: "
iam"
- Folder with "red" infrastructure – name:
“red”
This should look something like this (Azure example):
ℹ️ Note: In some cases, red or green infrastructure building is not possible.
Green and red infrastructure folders have the same structure:
- Resources that will be created;
- Default variables;
- Terraform vars (terraform.tfvars);
- Provider configuration for connecting with Azure account.
For example:
If you have a script with multiple resources, it is recommended to create separate files for the main of them.
For example, it will be easier for understanding to have separate files containing information about the storage (storage.tf), vault configuration (key_vault.tf), etc.
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
}
provider "azurerm" {
features {}
}
variable "prefix" {
type = string
}
variable "location" {
type = string
}
variable "tags" {
type = map(string)
}
prefix = <rule_number> # e.g. prefix = "348"
location = "eastus"
tags = {
CustodianRule = "ecc-azure-348-mysql_harden_usage_for_local_infile"
ComplianceStatus = "Green"
}
ℹ️ Note: All regions and locations are used as an example and can be changed depending on your case.
The "resource" block name should be assigned according to the recommendations below:
- If the resource module creates a single resource of a specific service, the resource name should be
this
:
resource "azurerm_app_service_certificate" "this" {
# ... remaining arguments omitted
}
- If you have multiple resources of a specific service, then more descriptive names should be assigned:
private
,public
, ormain
:
resource "azurerm_subnet" "public" {
# ... remaining arguments omitted
}
- If you have multiple resources but with the same or similar configurations, their functionality should be described in a comment before the resource name. You can also name them
bucket1
,bucket2
:
# resource description
resource "azurerm_role_definition" "role1" {
# ... remaining arguments omitted
}
Value of the argument "Name" must include:
- Rule ID;
- Resource description;
- Infrastructure identification:
"green"
or"red"
.
For example:
resource "azurerm_resource_group" "this" {
name = "002-rg-green"
location = "eastus"
}
In some cases, a "name" cannot include a symbol "_"
or be started with numbers and should look as follows:
resource "resource" "this" {
bucket = "resource-000-green"
}
resource "resource" "this" {
name = "000resourcegreen"
# ... remaining arguments omitted
}
Tags should contain the rule name and compliance status - "green" or "red":
resource "resource" "this" {
name = "000_resource_green"
# ... remaining arguments omitted
tags = {
CustodiaRule = "ecc-azure-000-example_policy"
Description = "..."
ComplianceStatus = "Green"
}
}
ℹ️ Note: The line containing description is optional
Tags, as well as meta-arguments "depends_on" and "lifecycle", should be separated by a single empty line:
resource "resource" "this" {
name = "000_resource_green"
# ... remaining arguments omitted
tags = {
CustodianRule = "ecc-azure-000-example_policy"
Description = "..."
ComplianceStatus = "Green"
}
depends_on = {
...
}
lifecycle = {
...
}
}
All passwords (IDs where possible) should be randomly generated:
resource "random_password" "this" {
length = 12
special = true
number = true
override_special = "!#$%\*()-_=+[]{}:?"
}
resource "azurerm_mssql_server" "this" {
# ... remaining arguments omitted
name = "014-azuresqlserver-green"
administrator_login_password = random_password.this.result
}