├── .github
└── ISSUE_TEMPLATE
│ └── feature_request.md
├── .gitignore
├── .vscode
└── settings.json
├── AzureDevops-Introduction
├── README.md
├── aks-k8s
│ ├── .gitignore
│ ├── README.md
│ ├── authentication.tf
│ ├── kubernetes.tf
│ ├── log_and_keyvault.tf
│ ├── output.tf
│ ├── secret
│ │ └── README.md
│ ├── var.tf
│ └── variable
│ │ ├── backend-jdld.tfvars
│ │ └── main-jdld.tfvars
└── azure-pipelines.yml
├── Best-Practice
├── Best-Practice.yml
├── BestPractice-1
│ ├── .gitignore
│ ├── README.md
│ ├── image
│ │ ├── done.png
│ │ ├── error.png
│ │ └── tfstate.png
│ ├── main.tf
│ ├── secret
│ │ └── README.md
│ └── var.tf
├── BestPractice-2
│ ├── .gitignore
│ ├── README.md
│ ├── image
│ │ ├── noversion.png
│ │ └── version.png
│ ├── main.tf
│ ├── secret
│ │ └── README.md
│ ├── var.tf
│ └── variable
│ │ └── main-jdld.tfvars
├── BestPractice-3
│ ├── .gitignore
│ ├── README.md
│ ├── image
│ │ ├── done.png
│ │ ├── explicit.png
│ │ └── implicit.png
│ ├── main.tf
│ ├── secret
│ │ └── README.md
│ ├── var.tf
│ └── variable
│ │ └── main-jdld.tfvars
├── BestPractice-4
│ ├── .gitignore
│ ├── README.md
│ ├── image
│ │ ├── depl.png
│ │ ├── destroy.png
│ │ └── nodestroy.png
│ ├── main.tf
│ ├── secret
│ │ └── README.md
│ ├── var.tf
│ └── variable
│ │ └── main-jdld.tfvars
└── README.md
├── CreateAzureRm-Infra
├── CreateAzureRm-Infra.yml
├── README.md
├── apps
│ ├── README.md
│ ├── main.tf
│ ├── variable
│ │ ├── apps-backend-jdld.tfvars
│ │ └── apps-main-jdld.tfvars
│ └── variables.tf
├── infra
│ ├── README.md
│ ├── application_gateway.tf
│ ├── main.tf
│ ├── variable
│ │ ├── infra-backend-jdld.tfvars
│ │ └── infra-main-jdld.tfvars
│ └── variables.tf
├── secret
│ └── README.md
├── tools
│ ├── bash_functions.sh
│ ├── handlebars.js
│ ├── mustache.min.js
│ ├── process_template.handlebar.js
│ └── process_template.js
└── workflow.png
├── LICENSE
├── README.md
├── _config.yml
├── _includes
└── google-analytics.html
├── azurerm_application_gateway
├── README.md
├── main.tf
└── provider.tf
├── module
├── Add-AzureRmLoadBalancerOutboundRules
│ ├── AzureRmLoadBalancerOutboundRules_template.json
│ ├── README.md
│ ├── main.tf
│ ├── output.tf
│ ├── var.tf
│ └── versions.tf
├── Az-Bastion
│ ├── AzureRmBastion_template.json
│ ├── README.md
│ ├── main.tf
│ ├── output.tf
│ ├── var.tf
│ └── versions.tf
├── Az-KeyVault
│ ├── README.md
│ ├── main.tf
│ ├── output.tf
│ ├── var.tf
│ └── versions.tf
├── Az-LoadBalancer
│ └── README.md
├── Az-PolicyAssignment
│ ├── README.md
│ ├── main.tf
│ ├── output.tf
│ ├── var.tf
│ └── versions.tf
├── Az-PolicyDefinition
│ ├── README.md
│ ├── json_files
│ │ ├── enforce-nsg-on-subnet_parameters.json
│ │ ├── enforce-nsg-on-subnet_policy_rule.json
│ │ ├── enforce-udr-on-subnet_parameters.json
│ │ └── enforce-udr-on-subnet_policy_rule.json
│ ├── main.tf
│ ├── output.tf
│ ├── var.tf
│ └── versions.tf
├── Az-RoleAssignment
│ ├── README.md
│ ├── main.tf
│ ├── output.tf
│ ├── var.tf
│ └── versions.tf
├── Az-RoleDefinition
│ ├── README.md
│ ├── main.tf
│ ├── output.tf
│ ├── var.tf
│ └── versions.tf
├── Az-VirtualNetwork
│ └── README.md
├── Az-Vm
│ └── README.md
├── Create-AzureRmPaloAltoAz
│ ├── AzureRmPaloAltoAz_template.json
│ ├── README.md
│ ├── main.tf
│ ├── output.tf
│ ├── var.tf
│ └── versions.tf
├── Create-AzureRmPolicyDefinition
│ └── json_files
│ │ └── enforce-udr-on-subnet_parameters.json
├── Create-AzureRmRecoveryServicesVault
│ ├── AzureRmRecoveryServicesVaultPolicy_template.json
│ ├── AzureRmRecoveryServicesVault_template.json
│ ├── README.md
│ ├── main.tf
│ ├── output.tf
│ ├── sample_withoutarmtemplate.tf
│ ├── var.tf
│ └── versions.tf
├── Enable-AzureRmVirtualNetworkPeering
│ ├── main.tf
│ ├── output.tf
│ ├── var.tf
│ └── versions.tf
└── Get-AzureRmVirtualNetwork
│ ├── README.md
│ ├── main.tf
│ ├── output.tf
│ ├── var.tf
│ └── versions.tf
└── pipeline
├── PublishBuildArtifacts.yml
└── terraform.yml
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | labels:
5 |
6 | ---
7 |
8 | **Is your feature request related to a problem? Please describe.**
9 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
10 |
11 | **Describe the solution you'd like**
12 | A clear and concise description of what you want to happen.
13 |
14 | **Describe alternatives you've considered**
15 | A clear and concise description of any alternative solutions or features you've considered.
16 |
17 | **Additional context**
18 | Add any other context or screenshots about the feature request here.
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled files
2 | *.tfstate
3 | *.tfstate.backup
4 | *.DS_Store
5 | *.tfstate.lock.info
6 |
7 | # Directory
8 | .terraform/
9 | **/test
10 | create_sa/
11 |
12 | # Secret files
13 | **/test.tf
14 | **/secret/*.json
15 | **/secret/*.tfvars
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.associations": {
3 | "*.tf": "terraform",
4 | "*.tfvars": "terraform",
5 | "*.tfstate": "json"
6 | },
7 | "editor.formatOnSave": true,
8 | "files.eol": "\n"
9 | }
--------------------------------------------------------------------------------
/AzureDevops-Introduction/README.md:
--------------------------------------------------------------------------------
1 | [Previous page >](../)
2 |
3 | [](https://dev.azure.com/jamesdld23/vpc_lab/_build/latest?definitionId=9&branchName=master)
4 |
5 | Content
6 | ------------
7 |
8 | Share articles about CI/CD, Azure DevOps and Terraform on Azure.
9 |
10 | Reference
11 | ------------
12 |
13 | | Link | Description |
14 | | ------------- | ------------- |
15 | | [Azure DevOps & Terraform - Concept](https://medium.com/@jamesdld23/a-cicd-journey-with-azure-devops-and-terraform-part-1-358f785b13f3) | CI/CD Concept |
16 | | [Azure DevOps & Terraform - Demo](https://medium.com/@jamesdld23/a-ci-cd-journey-with-azure-devops-and-terraform-part-2-524144511294) | Build your CI/CD pipeline for an Kubernetes cluster with Azure Kubernetes Service through Azure DevOps and Terraform 0.12 |
17 |
--------------------------------------------------------------------------------
/AzureDevops-Introduction/aks-k8s/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled files
2 | *.tfstate
3 | *.tfstate.backup
4 | *.DS_Store
5 |
6 | # Directory
7 | .terraform/
8 |
9 | # Secret files
10 | secret/*.tfvars
11 |
--------------------------------------------------------------------------------
/AzureDevops-Introduction/aks-k8s/README.md:
--------------------------------------------------------------------------------
1 | [Previous page >](../)
2 |
3 | Create a Kubernetes cluster with Azure Kubernetes Service and Terraform
4 | -----
5 |
6 | The Terraform code available here has been inspired from those Microsoft guides:
7 | 1. [Create a Kubernetes cluster with Azure Kubernetes Service and Terraform](https://docs.microsoft.com/en-us/azure/terraform/terraform-create-k8s-cluster-with-tf-and-aks)
8 | 2. [Quickstart: Deploy an Azure Kubernetes Service (AKS) cluster using the Azure CLI](https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough)
9 |
10 | With your Terraform template created, the first step is to initialize Terraform.
11 | This step ensures that Terraform has all the prerequisites to build your template in Azure.
12 |
13 | ```hcl
14 |
15 | terraform init -backend-config="./variable/backend-jdld.tfvars" -backend-config="./secret/backend-jdld.json" -reconfigure
16 |
17 | ```
18 |
19 | The terraform plan command is used to create an execution plan.
20 | This step compares the requested resources to the state information saved by Terraform and then gives as an output the planned execution. Resources are not created in Azure.
21 | ```hcl
22 |
23 | terraform plan -var-file="./variable/main-jdld.tfvars" -var-file="./secret/main-jdld.json"
24 |
25 | ```
26 |
27 | If all is ok with the proposal you can now apply the configuration.
28 | ```hcl
29 |
30 | terraform apply -var-file="./variable/main-jdld.tfvars" -var-file="./secret/main-jdld.json"
31 |
32 | ```
33 |
34 | To destroy the associated resources.
35 | ```hcl
36 |
37 | terraform destroy -var-file="./variable/main-jdld.tfvars" -var-file="./secret/main-jdld.json"
38 |
39 | ```
--------------------------------------------------------------------------------
/AzureDevops-Introduction/aks-k8s/authentication.tf:
--------------------------------------------------------------------------------
1 | #Set the terraform backend
2 | terraform {
3 | required_version = ">= 0.12.6"
4 |
5 | backend "azurerm" {} #Backend variables are initialized through the secret and variable folders
6 | }
7 |
8 | #Set the Provider
9 | provider "azurerm" {
10 | subscription_id = "${var.subscription_id}"
11 | client_id = "${var.client_id}"
12 | client_secret = "${var.client_secret}"
13 | tenant_id = "${var.tenant_id}"
14 | version = "~> 2.0"
15 | features {}
16 | }
17 |
18 | provider "azuread" {
19 | subscription_id = "${var.subscription_id}"
20 | client_id = "${var.client_id}"
21 | client_secret = "${var.client_secret}"
22 | tenant_id = "${var.tenant_id}"
23 | #version = "0.4.0"
24 | #Use the last version of the azurerm provider, reguraly tested by the following Azure DevOps pipeline : https://dev.azure.com/jamesdld23/vpc_lab/_build?definitionId=9&_a=summary
25 | }
26 |
27 | #Get the Resource Group and the SPN Id
28 |
29 | data "azurerm_resource_group" "Infr" {
30 | name = "${var.rg_infr_name}"
31 | }
32 |
33 | data "azuread_service_principal" "Infr" {
34 | application_id = "${var.client_id}"
35 | }
36 |
--------------------------------------------------------------------------------
/AzureDevops-Introduction/aks-k8s/kubernetes.tf:
--------------------------------------------------------------------------------
1 | #Call module/resource
2 | resource "azurerm_kubernetes_cluster" "demo" {
3 | name = var.kubernetes_cluster["name"]
4 | location = data.azurerm_resource_group.Infr.location
5 | resource_group_name = data.azurerm_resource_group.Infr.name
6 | dns_prefix = var.kubernetes_cluster["dns_prefix"]
7 |
8 | agent_pool_profile {
9 | name = "default"
10 | count = 1
11 | vm_size = "Standard_D1_v2"
12 | os_type = "Linux"
13 | os_disk_size_gb = 30
14 | }
15 |
16 | service_principal {
17 | client_id = var.client_id
18 | client_secret = var.client_secret
19 | }
20 |
21 | addon_profile {
22 | oms_agent {
23 | enabled = true
24 | log_analytics_workspace_id = azurerm_log_analytics_workspace.demo.id
25 | }
26 | }
27 |
28 | tags = data.azurerm_resource_group.Infr.tags
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/AzureDevops-Introduction/aks-k8s/log_and_keyvault.tf:
--------------------------------------------------------------------------------
1 | #Call module/resource
2 | ###
3 | # Use Log Monitor with AKS
4 | ###
5 | resource "azurerm_log_analytics_workspace" "demo" {
6 | name = var.log_analytics_workspace["name"]
7 | location = data.azurerm_resource_group.Infr.location
8 | resource_group_name = data.azurerm_resource_group.Infr.name
9 | sku = var.log_analytics_workspace["sku"]
10 | tags = data.azurerm_resource_group.Infr.tags
11 | }
12 |
13 | resource "azurerm_log_analytics_solution" "demo" {
14 | count = length(var.log_analytics_workspace.solutions)
15 | solution_name = var.log_analytics_workspace.solutions[count.index]["name"]
16 | location = azurerm_log_analytics_workspace.demo.location
17 | resource_group_name = data.azurerm_resource_group.Infr.name
18 | workspace_resource_id = azurerm_log_analytics_workspace.demo.id
19 | workspace_name = azurerm_log_analytics_workspace.demo.name
20 |
21 | plan {
22 | publisher = var.log_analytics_workspace.solutions[count.index]["publisher"]
23 | product = var.log_analytics_workspace.solutions[count.index]["product"]
24 | }
25 | }
26 |
27 | ###
28 | # Store the Kubernetes AKS cluster credential in the Key Vault
29 | ###
30 | data "azurerm_client_config" "current" {}
31 |
32 | resource "azurerm_key_vault" "demo" {
33 | name = var.key_vault["name"]
34 | location = data.azurerm_resource_group.Infr.location
35 | resource_group_name = data.azurerm_resource_group.Infr.name
36 | tenant_id = data.azurerm_client_config.current.tenant_id
37 | sku_name = var.key_vault["sku"]
38 |
39 | access_policy {
40 | tenant_id = data.azurerm_client_config.current.tenant_id
41 | object_id = data.azurerm_client_config.current.service_principal_object_id
42 |
43 | certificate_permissions = [
44 | "create",
45 | "delete",
46 | "deleteissuers",
47 | "get",
48 | "getissuers",
49 | "import",
50 | "list",
51 | "listissuers",
52 | "managecontacts",
53 | "manageissuers",
54 | "setissuers",
55 | "update",
56 | ]
57 |
58 | key_permissions = [
59 | "backup",
60 | "create",
61 | "decrypt",
62 | "delete",
63 | "encrypt",
64 | "get",
65 | "import",
66 | "list",
67 | "purge",
68 | "recover",
69 | "restore",
70 | "sign",
71 | "unwrapKey",
72 | "update",
73 | "verify",
74 | "wrapKey",
75 | ]
76 |
77 | secret_permissions = [
78 | "backup",
79 | "delete",
80 | "get",
81 | "list",
82 | "purge",
83 | "recover",
84 | "restore",
85 | "set",
86 | ]
87 | }
88 |
89 | tags = data.azurerm_resource_group.Infr.tags
90 | }
91 |
92 | resource "azurerm_key_vault_secret" "demo" {
93 | name = "providerkubernetes"
94 | value = azurerm_kubernetes_cluster.demo.kube_config.0.password
95 | key_vault_id = azurerm_key_vault.demo.id
96 |
97 | tags = {
98 | host = "${azurerm_kubernetes_cluster.demo.kube_config.0.host}"
99 | username = "${azurerm_kubernetes_cluster.demo.kube_config.0.username}"
100 | #client_certificate = "${base64decode(azurerm_kubernetes_cluster.demo.kube_config.0.client_certificate)}"
101 | #client_key = "${base64decode(azurerm_kubernetes_cluster.demo.kube_config.0.client_key)}"
102 | #cluster_ca_certificate = "${base64decode(azurerm_kubernetes_cluster.demo.kube_config.0.cluster_ca_certificate)}"
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/AzureDevops-Introduction/aks-k8s/output.tf:
--------------------------------------------------------------------------------
1 | #Terraform outputs allow you to define values that will be highlighted to the user when Terraform applies a plan, and can be queried using the terraform output command.
2 | output "provider_kubernetes" {
3 | value = {
4 | host = azurerm_kubernetes_cluster.demo.kube_config.0.host
5 | username = azurerm_kubernetes_cluster.demo.kube_config.0.username
6 | password = azurerm_kubernetes_cluster.demo.kube_config.0.password
7 | client_certificate = base64decode(azurerm_kubernetes_cluster.demo.kube_config.0.client_certificate)
8 | client_key = base64decode(azurerm_kubernetes_cluster.demo.kube_config.0.client_key)
9 | cluster_ca_certificate = base64decode(azurerm_kubernetes_cluster.demo.kube_config.0.cluster_ca_certificate)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/AzureDevops-Introduction/aks-k8s/secret/README.md:
--------------------------------------------------------------------------------
1 | Folder's golden rule
2 | ------------
3 | - Store secrets
4 | - File containing password are ignored by any git push
5 |
6 |
7 | Secret files
8 | ------------
9 | - backend-jdld.json : stores the credential to write Terraform backend files
10 | - Content sample ==>
11 | ```json
12 | {
13 | "tenant_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
14 | "subscription_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
15 | "client_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
16 | "client_secret": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
17 | }
18 | ```
19 | - main-jdld.json : stores the credential to write in Infra and Apps Resource groups and store the VM credentials
20 | - Content sample ==>
21 | ```json
22 | {
23 | "tenant_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
24 | "subscription_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
25 | "client_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
26 | "client_secret": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
27 | }
28 | ```
--------------------------------------------------------------------------------
/AzureDevops-Introduction/aks-k8s/var.tf:
--------------------------------------------------------------------------------
1 | #Variables declaration
2 |
3 | variable "subscription_id" {
4 | description = "Azure subscription Id."
5 | }
6 |
7 | variable "tenant_id" {
8 | description = "Azure tenant Id."
9 | }
10 |
11 | variable "client_id" {
12 | description = "Azure service principal application Id"
13 | }
14 |
15 | variable "client_secret" {
16 | description = "Azure service principal application Secret"
17 | }
18 |
19 | variable "rg_infr_name" {
20 | description = "Resource group name that will host our services"
21 | }
22 |
23 | variable log_analytics_workspace {
24 | description = "The log analytics workspace"
25 | type = any
26 | /* Following code is currently raising an error
27 | map(object({
28 |
29 | name=string #The log analytics workspace name must be unique
30 | sku=string #Refer https://azure.microsoft.com/pricing/details/monitor/ for log analytics pricing
31 |
32 | solutions = list(object({
33 | name = string
34 | publisher = string
35 | product = string
36 | }))
37 | }))*/
38 | }
39 |
40 | variable "key_vault" {
41 | description = "The key vault"
42 | type = map(string)
43 | # type = map(object({
44 | # name = string #The key vault name must be unique
45 | # sku = string
46 | # }))
47 | }
48 |
49 | variable "kubernetes_cluster" {
50 | description = "Kubernetes cluster with Azure Kubernetes Service"
51 | type = any
52 | }
53 |
--------------------------------------------------------------------------------
/AzureDevops-Introduction/aks-k8s/variable/backend-jdld.tfvars:
--------------------------------------------------------------------------------
1 | #Variables initialization
2 | resource_group_name = "infr-jdld-noprd-rg1"
3 |
4 | storage_account_name = "infrasdbx1vpcjdld1" #Name must be unique
5 |
6 | container_name = "tfstate"
7 |
8 | key = "aks-k8s.tfstate"
9 |
--------------------------------------------------------------------------------
/AzureDevops-Introduction/aks-k8s/variable/main-jdld.tfvars:
--------------------------------------------------------------------------------
1 | #Variables initialization
2 |
3 | ###
4 | # Core
5 | ###
6 |
7 | rg_infr_name = "infr-jdld-noprd-rg1"
8 |
9 | ###
10 | # Log & Certificate
11 | ###
12 |
13 | log_analytics_workspace = {
14 | name = "demojdldsdbxlogmon1" #The log analytics workspace name must be unique
15 | sku = "PerGB2018" #Refer to https://azure.microsoft.com/pricing/details/monitor/ for log analytics pricing
16 |
17 | solutions = [{
18 | name = "ContainerInsights"
19 | publisher = "Microsoft"
20 | product = "OMSGallery/ContainerInsights"
21 | }]
22 | }
23 |
24 | key_vault = {
25 | name = "demojdldsdbxkeyv2" #The kay vault name must be unique
26 | sku = "standard"
27 | }
28 |
29 | ###
30 | # AKS
31 | ###
32 | kubernetes_cluster = {
33 | name = "aks-demo-jdld"
34 | dns_prefix = "aksdemojdld" #Please see https://aka.ms/aks-naming-rules
35 | }
36 |
--------------------------------------------------------------------------------
/AzureDevops-Introduction/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | #Multi-stage YAML pipeline demo.
2 | name: $(BuildDefinitionName).$(DayOfYear)$(Rev:.r)
3 |
4 | schedules:
5 | - cron: "45 6 * * 4"
6 | branches:
7 | include:
8 | - master
9 | displayName: Weekly Thursday 6h45 am UTC build
10 | always: true
11 |
12 | variables:
13 | - name: terraform_version
14 | value: "0.12.13"
15 | - name: vmImageName
16 | value: "ubuntu-latest"
17 | - name: backend_main_secret_file_id1 # secret id located in your Azure DevOps library, file used by the following cmdlet Terraform init, plan, apply and destroy
18 | value: "backend-main-jdld-1.json"
19 | - name: artifact_name
20 | value: "AzureDevops-Introduction"
21 |
22 | resources:
23 | repositories:
24 | - repository: Yaml_Templates # identifier (A-Z, a-z, 0-9, and underscore)
25 | type: git #git refers to Azure Repos Git repos
26 | name: Template/template_pipeline #To refer to a repo in another project within the same organization, prefix the name with that project's name. For example, OtherProject/otherRepo.
27 | ref: refs/tags/0.1.0 # ref name to use, defaults to 'refs/heads/master'
28 |
29 | trigger:
30 | batch: true # when a build is running, the system waits until the build is completed
31 | branches:
32 | include:
33 | - master
34 | - feature/*
35 | - release/*
36 | paths:
37 | include:
38 | - AzureDevops-Introduction/*
39 |
40 | stages:
41 | - stage: Build
42 | jobs:
43 | - job: Terraform_Plan
44 | displayName: Terraform Plan - Publish a package if Infrastructure changes are identified
45 | continueOnError: false
46 | pool:
47 | vmImage: $(vmImageName)
48 | steps:
49 | - task: DownloadSecureFile@1
50 | displayName: "Download secure file $(backend_main_secret_file_id1)"
51 | inputs:
52 | secureFile: $(backend_main_secret_file_id1)
53 |
54 | - template: terraform.yml@Yaml_Templates
55 | parameters:
56 | version: $(terraform_version)
57 | path: "./AzureDevops-Introduction/aks-k8s/"
58 | package_name: "aks-k8s"
59 | terraform_init: true
60 | terraform_plan: true
61 | backend_secret_file_id: $(backend_main_secret_file_id1)
62 | backend_file_path: "variable/backend-jdld.tfvars"
63 | main_secret_file_id: $(backend_main_secret_file_id1)
64 | main_file_path: "variable/main-jdld.tfvars"
65 |
66 | - publish: "./ArtifactPublishLocation" # Local path to include in the Artifact
67 | artifact: "$(artifact_name)"
68 |
69 | - stage: Deploy
70 | dependsOn: Build
71 | jobs:
72 | # track deployments on the environment
73 | - deployment: Terraform_Apply
74 | displayName: Terraform Apply - Resources creation
75 | pool:
76 | vmImage: $(vmImageName)
77 | environment: "Terraform_Apply"
78 | strategy:
79 | # default deployment strategy
80 | runOnce:
81 | deploy:
82 | steps:
83 | - template: terraform.yml@Yaml_Templates
84 | parameters:
85 | version: $(terraform_version)
86 | artifact_path: $(Pipeline.Workspace)/$(artifact_name)
87 | package_name: "aks-k8s"
88 | terraform_apply: true
89 | main_file_path: "variable/main-jdld.tfvars"
90 |
91 | - stage: Deliver
92 | dependsOn: Deploy
93 | jobs:
94 | # track deployments on the environment
95 | - deployment: Terraform_Destroy
96 | displayName: Terraform Destroy - Script ok, now deleting the resources
97 | pool:
98 | vmImage: $(vmImageName)
99 | environment: "Terraform_Destroy"
100 | strategy:
101 | # default deployment strategy
102 | runOnce:
103 | deploy:
104 | steps:
105 | - task: DownloadSecureFile@1
106 | displayName: Download secure file $(backend_main_secret_file_id1)
107 | inputs:
108 | secureFile: $(backend_main_secret_file_id1)
109 |
110 | - template: terraform.yml@Yaml_Templates
111 | parameters:
112 | version: $(terraform_version)
113 | artifact_path: $(Pipeline.Workspace)/$(artifact_name)
114 | package_name: "aks-k8s"
115 | terraform_destroy: true
116 | main_secret_file_id: $(backend_main_secret_file_id1)
117 | main_file_path: "variable/main-jdld.tfvars"
118 |
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-1/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled files
2 | *.tfstate
3 | *.tfstate.backup
4 | *.DS_Store
5 |
6 | # Directory
7 | .terraform/
8 |
9 | # Secret files
10 | secret/*.tfvars
11 |
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-1/README.md:
--------------------------------------------------------------------------------
1 | [Previous page >](../)
2 |
3 | Best Practice 1
4 | ------------
5 | Use remote backend.
6 | In this article we will perform the following action with and without a remote backend :
7 | 1. Create a Virtual Network
8 | 2. Assign a Role Definition to the Virtual Network
9 |
10 | Through the second action we will be able to demonstrate one of the reason why we should use a remote backend : some resources like "azurerm_role_assignment" couldn't update their tfstate if the "role assignment" is already done. [This article on GitHub](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1857) describes this topic.
11 |
12 | In other words ... if you don't have the tfstate, your Terraform `apply` could fail and the only way to re adjust it would be :
13 | 1. to find this tfstate back (would have been good to have it on a remote location) or
14 | 2. to perform Terraform import on the the resources that you can't integrate back in your tfstate.
15 |
16 |
17 | ### Prerequisite
18 | -----
19 |
20 | | Item | Description |
21 | | ------------- | ------------- |
22 | | Azure Subscription | An Azure subscription id |
23 | | Resource Group | An Azure resource group is available |
24 | | Storage Account | An Azure storage account is available and is located in the upper resource group, it contains a container named `tfstate` |
25 | | Service Principal | An Azure service principal is available and has the `owner` privilege on the upper resource group |
26 | | Terraform file | [Clone this repository](https://github.com/JamesDLD/terraform/tree/master/Best-Practice/BestPractice-1) and fill in the following files with the upper prerequisite items :
Variable used for the Terraform `init` : secret/backend-jdld.json
Variable used for the Terraform `plan` and `apply` : [main.tf](main.tf) & secret/main-jdld.json |
27 |
28 |
29 | What should we do?
30 | ------------
31 | We will create the upper mentioned element using remote backend.
32 |
33 | In the following article our remote backend will be located on an Azure Storage Account and we will use a service principal to write on this storage account.
34 | To do so we will have to declare the following bracket in our Terraform tf file.
35 | ```hcl
36 | terraform {
37 | backend "azurerm" {
38 | storage_account_name = "infrasdbx1vpcjdld1"
39 | container_name = "tfstate"
40 | key = "BestPractice-1.tfstate"
41 | resource_group_name = "infr-jdld-noprd-rg1"
42 | subscription_id = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
43 | client_id = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
44 | client_secret = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
45 | tenant_id = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
46 | }
47 | }
48 | ```
49 |
50 |
51 |
52 | ### 1. Usage
53 | -----
54 |
55 | This step ensures that Terraform has all the prerequisites to build your template in Azure.
56 | ```hcl
57 | terraform init -backend-config="secret/backend-jdld.json" -reconfigure
58 | ```
59 |
60 | The terraform plan command is used to create an execution plan.
61 | This step compares the requested resources to the state information saved by Terraform and then gives as an output the planned execution. Resources are not created in Azure.
62 | ```hcl
63 | terraform plan -var-file="secret/main-jdld.json"
64 | ```
65 |
66 | If all is ok with the proposal you can now apply the configuration.
67 | ```hcl
68 | terraform apply -var-file="secret/main-jdld.json"
69 | ```
70 |
71 | ### 2. Analysis
72 | -----
73 |
74 | | Description | Screenshot |
75 | | ------------- | ------------- |
76 | | Our objects have been created on Azure |  |
77 | | Our remote backend has generated a Terraform tfstate with all our objects specifications |  |
78 |
79 |
80 | What shouldn't we do?
81 | ------------
82 | We will now forget the backend specification.
83 | This will imply that we will no longer depend on a remote backend, in other words we will not be aware of any other deployment of someone else.
84 |
85 | In here we will demonstrate that remote backend encourage collaboration.
86 |
87 | Let's remove the following bracket : backend "azurerm" {}.
88 | The top part of our [main.tf](main.tf) script will look like the following :
89 | ```hcl
90 | terraform {
91 | }
92 | ```
93 |
94 | ### 1. Usage
95 | -----
96 |
97 | This step ensures that Terraform has all the prerequisites to build your template in Azure.
98 | ```hcl
99 | terraform init -backend-config="secret/backend-jdld.json" -reconfigure
100 | ```
101 | > When the message `Do you want to copy existing state to the new backend?` appears enter `no` and press enter.
102 |
103 | You can now apply the configuration.
104 | ```hcl
105 | terraform apply -var-file="secret/main-jdld.json"
106 | ```
107 |
108 | ### 2. Analysis
109 | -----
110 |
111 | | Description | Screenshot |
112 | | ------------- | ------------- |
113 | | Our objects are still on Azure |  |
114 | | The Terraform fails because for some resource like "azurerm_role_assignment" we can’t automatically
update our tfstate telling that the resource is already created as we wished.
Note that it wasn’t
the case for the resource "azurerm_virtual_network" |  |
115 |
116 |
117 |
118 | See you!
119 |
120 | JamesDLD
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-1/image/done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JamesDLD/terraform/0c71dce4e1814ff2cf5fbeaa99fe0f6e983db9fb/Best-Practice/BestPractice-1/image/done.png
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-1/image/error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JamesDLD/terraform/0c71dce4e1814ff2cf5fbeaa99fe0f6e983db9fb/Best-Practice/BestPractice-1/image/error.png
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-1/image/tfstate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JamesDLD/terraform/0c71dce4e1814ff2cf5fbeaa99fe0f6e983db9fb/Best-Practice/BestPractice-1/image/tfstate.png
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-1/main.tf:
--------------------------------------------------------------------------------
1 | #Set the terraform backend
2 | terraform {
3 | backend "azurerm" {
4 | storage_account_name = "infrasdbx1vpcjdld1"
5 | container_name = "tfstate"
6 | key = "BestPractice-1.tfstate"
7 | resource_group_name = "infr-jdld-noprd-rg1"
8 | }
9 | }
10 |
11 | #Set the Provider
12 | provider "azurerm" {
13 | subscription_id = var.subscription_id
14 | client_id = var.client_id
15 | client_secret = var.client_secret
16 | tenant_id = var.tenant_id
17 | version = "~> 2.0"
18 | features {}
19 | }
20 |
21 | #Variables
22 | variable "subnets" {
23 | description = "Subnets list."
24 |
25 | type = list(object({
26 | name = string
27 | address_prefix = string
28 | }))
29 |
30 | default = [
31 | {
32 | name = "bp1-front-snet1"
33 | address_prefix = "10.0.1.0/24"
34 | },
35 | {
36 | name = "bp1-front-snet2"
37 | address_prefix = "10.0.6.0/24"
38 | }
39 | ]
40 | }
41 |
42 |
43 | #Call module/resource
44 | resource "azurerm_virtual_network" "bp1-vnet1" {
45 | name = "bp1-vnet1"
46 | location = "westeurope"
47 | resource_group_name = "infr-jdld-noprd-rg1"
48 | address_space = ["10.0.0.0/16"]
49 |
50 | dynamic "subnet" {
51 | for_each = var.subnets
52 |
53 | content {
54 | name = lookup(subnet.value, "name", null)
55 | address_prefix = lookup(subnet.value, "address_prefix", null)
56 | }
57 | }
58 |
59 | tags = {
60 | environment = "Test"
61 | }
62 | }
63 |
64 | data "azurerm_subscription" "primary" {
65 | }
66 |
67 | data "azurerm_client_config" "test" {
68 | }
69 |
70 | resource "azurerm_role_assignment" "test" {
71 | scope = azurerm_virtual_network.bp1-vnet1.id
72 | role_definition_name = "Reader"
73 | principal_id = data.azurerm_client_config.test.service_principal_object_id
74 | }
75 |
76 | output "subnet_ids" {
77 | description = "The subnet Ids."
78 | value = azurerm_virtual_network.bp1-vnet1.subnet.*.id
79 | }
80 |
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-1/secret/README.md:
--------------------------------------------------------------------------------
1 | Folder's golden rule
2 | ------------
3 | - Store secrets
4 | - File containing password are ignored by any git push
5 |
6 |
7 | Secret files
8 | ------------
9 | - backend-jdld.json : stores the credential to write Terraform backend files
10 | - Content sample ==>
11 | ```json
12 | {
13 | "tenant_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
14 | "subscription_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
15 | "client_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
16 | "client_secret": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
17 | }
18 | ```
19 | - main-jdld.json : stores the credential to write in Infra and Apps Resource groups and store the VM credentials
20 | - Content sample ==>
21 | ```json
22 | {
23 | "tenant_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
24 | "subscription_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
25 | "client_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
26 | "client_secret": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
27 | }
28 | ```
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-1/var.tf:
--------------------------------------------------------------------------------
1 | #Variables declaration
2 |
3 | variable "subscription_id" {
4 | description = "Azure subscription Id."
5 | }
6 |
7 | variable "tenant_id" {
8 | description = "Azure tenant Id."
9 | }
10 |
11 | variable "client_id" {
12 | description = "Azure service principal application Id"
13 | }
14 |
15 | variable "client_secret" {
16 | description = "Azure service principal application Secret"
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-2/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled files
2 | *.tfstate
3 | *.tfstate.backup
4 | *.DS_Store
5 |
6 | # Directory
7 | .terraform/
8 |
9 | # Secret files
10 | secret/*.tfvars
11 |
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-2/README.md:
--------------------------------------------------------------------------------
1 | [Previous page >](../)
2 |
3 | Best Practice 2
4 | ------------
5 | In this article we will see how to set Terraform, provider and modules version (see [this article](https://www.terraform.io/docs/configuration/terraform.html) from terraform.io website to learn more about managing Terraform version).
6 |
7 | In this article we will perform the following action :
8 | 1. Get a Resource Group
9 | 2. Create a Virtual Network and a Subnet in this Virtual Network
10 |
11 |
12 | ### Prerequisite
13 | -----
14 |
15 | | Item | Description |
16 | | ------------- | ------------- |
17 | | Azure Subscription | An Azure subscription id |
18 | | Resource Group | An Azure resource group is available |
19 | | Storage Account | An Azure storage account is available and is located in the upper resource group, it contains a container named `tfstate` |
20 | | Service Principal | An Azure service principal is available and has the `owner` privilege on the upper resource group |
21 | | Terraform file | [Clone this repository](https://github.com/JamesDLD/terraform/tree/master/Best-Practice/BestPractice-2) and fill in the following files with the upper prerequisite items :
Variable used for the Terraform `init` : secret/backend-jdld.json
Variable used for the Terraform `plan` and `apply` : [main.tf](main.tf) & [main-jdld.tfvars](variable/main-jdld.tfvars) & secret/main-jdld.json |
22 |
23 |
24 |
25 | What should we do?
26 | ------------
27 | We will create the upper mentioned element using remote backend (see the previous article [BestPractice-1](../BestPractice-1) for more information about remote backend).
28 |
29 | The Terraform executable file, the AzureRm provider and our modules version will be set as described in the following bracket (also available in our [main-jdld.tf](main-jdld.tf) Terraform file).
30 |
31 |
32 | Declare the Terraform required version
33 | ```hcl
34 | terraform {
35 | required_version = ">= 0.12.6"
36 |
37 | backend "azurerm" {
38 | storage_account_name = "infrasdbx1vpcjdld1"
39 | container_name = "tfstate"
40 | key = "BestPractice-2.tfstate"
41 | resource_group_name = "infr-jdld-noprd-rg1"
42 | }
43 | }
44 | ```
45 |
46 | Specify the AzureRm version
47 | ```hcl
48 | provider "azurerm" {
49 | version = ">= 1.31.0"
50 | subscription_id = "${var.subscription_id}"
51 | client_id = "${var.client_id}"
52 | client_secret = "${var.client_secret}"
53 | tenant_id = "${var.tenant_id}"
54 | version = "~> 2.0"
55 | features {}
56 | }
57 | ```
58 |
59 | Specify the module version
60 | ```hcl
61 | module "Az-VirtualNetwork" {
62 | source = "JamesDLD/Az-VirtualNetwork/azurerm"
63 | version = "0.1.4"
64 | net_prefix = "demo"
65 | network_resource_group_name = data.azurerm_resource_group.bp2.name
66 | virtual_networks = var.virtual_networks
67 | subnets = var.subnets
68 | route_tables = {}
69 | network_security_groups = {}
70 | }
71 | ```
72 |
73 |
74 |
75 | ### 1. Usage
76 | -----
77 |
78 | This step ensures that Terraform has all the prerequisites to build your template in Azure.
79 | ```hcl
80 | terraform init -backend-config="secret/backend-jdld.json" -reconfigure
81 | ```
82 |
83 | The terraform plan command is used to create an execution plan.
84 | This step compares the requested resources to the state information saved by Terraform and then gives as an output the planned execution. Resources are not created in Azure.
85 | ```hcl
86 | terraform plan -var-file="secret/main-jdld.json" -var-file="variable/main-jdld.tfvars"
87 | ```
88 |
89 | If all is ok with the proposal you can now apply the configuration.
90 | ```hcl
91 | terraform apply -var-file="secret/main-jdld.json" -var-file="variable/main-jdld.tfvars"
92 | ```
93 |
94 | ### 2. Analysis
95 | -----
96 |
97 | | Description | Screenshot |
98 | | ------------- | ------------- |
99 | | The Terraform `init` highlights our version |  |
100 | | Check the Terraform init when you remove the version |  |
101 |
102 |
103 | See you!
104 |
105 | JamesDLD
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-2/image/noversion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JamesDLD/terraform/0c71dce4e1814ff2cf5fbeaa99fe0f6e983db9fb/Best-Practice/BestPractice-2/image/noversion.png
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-2/image/version.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JamesDLD/terraform/0c71dce4e1814ff2cf5fbeaa99fe0f6e983db9fb/Best-Practice/BestPractice-2/image/version.png
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-2/main.tf:
--------------------------------------------------------------------------------
1 | #Set the terraform backend
2 | terraform {
3 | required_version = ">= 0.12.6"
4 |
5 | backend "azurerm" {
6 | storage_account_name = "infrasdbx1vpcjdld1"
7 | container_name = "tfstate"
8 | key = "BestPractice-2.tfstate"
9 | resource_group_name = "infr-jdld-noprd-rg1"
10 | }
11 | }
12 |
13 | #Set the Provider
14 | provider "azurerm" {
15 | version = ">= 1.31.0" #Use the last version tested through an Azure DevOps pipeline here : https://dev.azure.com/jamesdld23/vpc_lab/_build
16 | subscription_id = var.subscription_id
17 | client_id = var.client_id
18 | client_secret = var.client_secret
19 | tenant_id = var.tenant_id
20 | version = "~> 2.0"
21 | features {}
22 | }
23 |
24 | #Call module/resource
25 | #Get components
26 | data "azurerm_resource_group" "bp2" {
27 | name = "infr-jdld-noprd-rg1"
28 | }
29 |
30 | #Action
31 | module "Az-VirtualNetwork-demo" {
32 | source = "JamesDLD/Az-VirtualNetwork/azurerm"
33 | version = "0.1.4"
34 | net_prefix = "demo"
35 | network_resource_group_name = data.azurerm_resource_group.bp2.name
36 | virtual_networks = {
37 | vnet1 = {
38 | id = "1"
39 | prefix = "bp2"
40 | address_space = ["198.18.1.0/24", "198.18.2.0/24"]
41 | }
42 | }
43 | subnets = var.subnets
44 | route_tables = {}
45 | network_security_groups = {}
46 | }
47 |
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-2/secret/README.md:
--------------------------------------------------------------------------------
1 | Folder's golden rule
2 | ------------
3 | - Store secrets
4 | - File containing password are ignored by any git push
5 |
6 |
7 | Secret files
8 | ------------
9 | - backend-jdld.json : stores the credential to write Terraform backend files
10 | - Content sample ==>
11 | ```json
12 | {
13 | "tenant_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
14 | "subscription_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
15 | "client_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
16 | "client_secret": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
17 | }
18 | ```
19 | - main-jdld.json : stores the credential to write in Infra and Apps Resource groups and store the VM credentials
20 | - Content sample ==>
21 | ```json
22 | {
23 | "tenant_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
24 | "subscription_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
25 | "client_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
26 | "client_secret": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
27 | }
28 | ```
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-2/var.tf:
--------------------------------------------------------------------------------
1 | #Variables declaration
2 |
3 | variable "subscription_id" {
4 | description = "Azure subscription Id."
5 | }
6 |
7 | variable "tenant_id" {
8 | description = "Azure tenant Id."
9 | }
10 |
11 | variable "client_id" {
12 | description = "Azure service principal application Id"
13 | }
14 |
15 | variable "client_secret" {
16 | description = "Azure service principal application Secret"
17 | }
18 |
19 | variable "subnets" {
20 | description = "Subnet list."
21 | type = any
22 | }
23 |
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-2/variable/main-jdld.tfvars:
--------------------------------------------------------------------------------
1 | #Variables initialization
2 |
3 | subnets = {
4 | snet1 = {
5 | vnet_key = "vnet1" #(Mandatory)
6 | name = "bp2" #(Mandatory)
7 | address_prefix = "198.18.1.0/26" #(Mandatory)
8 | service_endpoints = ["Microsoft.Sql", "Microsoft.Storage"] #(Optional) delete this line for no Service Endpoints
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-3/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled files
2 | *.tfstate
3 | *.tfstate.backup
4 | *.DS_Store
5 |
6 | # Directory
7 | .terraform/
8 |
9 | # Secret files
10 | secret/*.tfvars
11 |
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-3/README.md:
--------------------------------------------------------------------------------
1 | [Previous page >](../)
2 |
3 | Best Practice 3
4 | ------------
5 | Implicit dependencies should be used whenever possible (see [this article](https://www.terraform.io/intro/getting-started/dependencies.html) from terraform.io website for more information).
6 | In this article we will perform the following action with *implicit* dependencies and with *explicit* dependencies :
7 | 1. Create 1 load balancer
8 | 2. Create 2 network interfaces and link them to a load balancer
9 |
10 |
11 | ### Prerequisite
12 | -----
13 |
14 | | Item | Description |
15 | | ------------- | ------------- |
16 | | Azure Subscription | An Azure subscription id |
17 | | Resource Group | An Azure resource group is available |
18 | | Storage Account | An Azure storage account is available and is located in the upper resource group, it contains a container named `tfstate` |
19 | | Service Principal | An Azure service principal is available and has the `owner` privilege on the upper resource group |
20 | | Terraform file | [Clone this repository](https://github.com/JamesDLD/terraform/tree/master/Best-Practice/BestPractice-3) and fill in the following files with the upper prerequisite items :
Variable used for the Terraform `init` : secret/backend-jdld.json
Variable used for the Terraform `plan` and `apply` : [main.tf](main.tf) & [main-jdld.tfvars](variable/main-jdld.tfvars) & secret/main-jdld.json |
21 |
22 |
23 |
24 | What should we do?
25 | ------------
26 | We will create the upper mentioned element using remote backend (see the previous article [BestPractice-1](../BestPractice-1) for more information about remote backend).
27 |
28 | Review the code [main.tf](main.tf), as illustrated in the following bracket, the *implicit* and the *explicit* methods are highlighted.
29 | ```hcl
30 | module "Az-Vm-Demo" {
31 | source = "JamesDLD/Az-Vm/azurerm"
32 | version = "0.1.2"
33 | sa_bootdiag_storage_uri = data.azurerm_storage_account.bp3.primary_blob_endpoint #(Mandatory)
34 | subnets_ids = module.Az-VirtualNetwork-Demo.subnet_ids #(Mandatory)
35 | linux_vms = {} #(Mandatory)
36 | windows_vms = var.vms #(Mandatory)
37 |
38 | #This an implicit dependency
39 | internal_lb_backend_ids = module.Create-AzureRmLoadBalancer-Demo.lb_backend_ids
40 |
41 | #This an explicit dependency
42 | #internal_lb_backend_ids = ["/subscriptions/${var.subscription_id}/resourceGroups/infr-jdld-noprd-rg1/providers/Microsoft.Network/loadBalancers/demo-bp3-internal-lb1/backendAddressPools/demo-bp3-internal-bckpool1"]
43 |
44 | vm_resource_group_name = data.azurerm_resource_group.bp3.name
45 | vm_prefix = "bp3" #(Optional)
46 | windows_storage_image_reference = var.windows_storage_image_reference #(Optional)
47 | admin_username = var.app_admin
48 | admin_password = var.pass
49 | vm_additional_tags = { bp = "3" } #(Optional)
50 | }
51 | ```
52 |
53 |
54 |
55 | ### 1. Usage
56 | -----
57 |
58 | This step ensures that Terraform has all the prerequisites to build your template in Azure.
59 | ```hcl
60 | terraform init -backend-config="secret/backend-jdld.json" -reconfigure
61 | ```
62 |
63 | The Terraform plan command is used to create an execution plan.
64 | This step compares the requested resources to the state information saved by Terraform and then gives as an output the planned execution. Resources are not created in Azure.
65 | ```hcl
66 | terraform plan -var-file="secret/main-jdld.json" -var-file="variable/main-jdld.tfvars"
67 | ```
68 |
69 | If all is ok with the proposal you can now apply the configuration with both methods (implicit and explicit) to track the impact.
70 | ```hcl
71 | terraform apply -var-file="secret/main-jdld.json" -var-file="variable/main-jdld.tfvars"
72 | ```
73 |
74 | We will now destroy what we have done with both methods (implicit and explicit) to track the impact.
75 | ```hcl
76 | terraform destroy -var-file="secret/main-jdld.json" -var-file="variable/main-jdld.tfvars"
77 | ```
78 |
79 | ### 2. Analysis
80 | -----
81 |
82 | | Description | Screenshot |
83 | | ------------- | ------------- |
84 | | Our 2 network interfaces are linked to the Load Balancer |  |
85 | | When using implicit dependencies all is working like a charm |  |
86 | | When using explicit dependencies we receive error(s) because some resources doesn't wait for
other to be created (a workaround consists in using the variable `depend on` but this will still
cause error when you will proceed Terraform `destroy`) |  |
87 |
88 |
89 | See you!
90 |
91 | JamesDLD
92 |
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-3/image/done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JamesDLD/terraform/0c71dce4e1814ff2cf5fbeaa99fe0f6e983db9fb/Best-Practice/BestPractice-3/image/done.png
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-3/image/explicit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JamesDLD/terraform/0c71dce4e1814ff2cf5fbeaa99fe0f6e983db9fb/Best-Practice/BestPractice-3/image/explicit.png
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-3/image/implicit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JamesDLD/terraform/0c71dce4e1814ff2cf5fbeaa99fe0f6e983db9fb/Best-Practice/BestPractice-3/image/implicit.png
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-3/main.tf:
--------------------------------------------------------------------------------
1 | #Set the terraform backend
2 | terraform {
3 | required_version = ">= 0.12.6"
4 |
5 | backend "azurerm" {
6 | storage_account_name = "infrasdbx1vpcjdld1"
7 | container_name = "tfstate"
8 | key = "BestPractice-3.tfstate"
9 | resource_group_name = "infr-jdld-noprd-rg1"
10 | }
11 | }
12 |
13 | #Set the Provider
14 | provider "azurerm" {
15 | version = "~> 2.0"
16 | subscription_id = var.subscription_id
17 | client_id = var.client_id
18 | client_secret = var.client_secret
19 | tenant_id = var.tenant_id
20 | features {}
21 | }
22 |
23 | #Call module/resource
24 | #Get components
25 |
26 | data "azurerm_resource_group" "bp3" {
27 | name = "infr-jdld-noprd-rg1"
28 | }
29 |
30 | data "azurerm_storage_account" "bp3" {
31 | name = "infrasdbx1vpcjdld1"
32 | resource_group_name = data.azurerm_resource_group.bp3.name
33 | }
34 |
35 | #Action
36 | module "Az-VirtualNetwork-Demo" {
37 | source = "JamesDLD/Az-VirtualNetwork/azurerm"
38 | version = "0.1.4"
39 | net_prefix = "demo"
40 | network_resource_group_name = data.azurerm_resource_group.bp3.name
41 | virtual_networks = {
42 | vnet1 = {
43 | id = "1"
44 | prefix = "bp3"
45 | address_space = ["10.0.3.0/24"]
46 | }
47 | }
48 | subnets = var.subnets
49 | route_tables = {}
50 | network_security_groups = {}
51 | }
52 |
53 | module "Create-AzureRmLoadBalancer-Demo" {
54 | source = "JamesDLD/Az-LoadBalancer/azurerm"
55 | version = "0.1.1"
56 | Lbs = var.Lbs
57 | LbRules = {}
58 | lb_prefix = "demo-bp3"
59 | lb_location = element(module.Az-VirtualNetwork-Demo.vnet_locations, 0)
60 | lb_resource_group_name = data.azurerm_resource_group.bp3.name
61 | Lb_sku = "basic"
62 | subnets_ids = module.Az-VirtualNetwork-Demo.subnet_ids
63 | lb_additional_tags = { bp = "3" }
64 | }
65 |
66 | module "Az-Vm-Demo" {
67 | source = "JamesDLD/Az-Vm/azurerm"
68 | version = "0.1.2"
69 | sa_bootdiag_storage_uri = data.azurerm_storage_account.bp3.primary_blob_endpoint #(Mandatory)
70 | subnets_ids = module.Az-VirtualNetwork-Demo.subnet_ids #(Mandatory)
71 | linux_vms = {} #(Mandatory)
72 | windows_vms = var.vms #(Mandatory)
73 |
74 | #This an implicit dependency
75 | internal_lb_backend_ids = module.Create-AzureRmLoadBalancer-Demo.lb_backend_ids
76 |
77 | #This an explicit dependency
78 | #internal_lb_backend_ids = ["/subscriptions/${var.subscription_id}/resourceGroups/infr-jdld-noprd-rg1/providers/Microsoft.Network/loadBalancers/demo-bp3-internal-lb1/backendAddressPools/demo-bp3-internal-bckpool1"]
79 |
80 | vm_resource_group_name = data.azurerm_resource_group.bp3.name
81 | vm_prefix = "bp3" #(Optional)
82 | windows_storage_image_reference = var.windows_storage_image_reference #(Optional)
83 | admin_username = var.app_admin
84 | admin_password = var.pass
85 | vm_additional_tags = { bp = "3" } #(Optional)
86 | }
87 |
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-3/secret/README.md:
--------------------------------------------------------------------------------
1 | Folder's golden rule
2 | ------------
3 | - Store secrets
4 | - File containing password are ignored by any git push
5 |
6 |
7 | Secret files
8 | ------------
9 | - backend-jdld.json : stores the credential to write Terraform backend files
10 | - Content sample ==>
11 | ```json
12 | {
13 | "tenant_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
14 | "subscription_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
15 | "client_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
16 | "client_secret": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
17 | }
18 | ```
19 | - main-jdld.json : stores the credential to write in Infra and Apps Resource groups and store the VM credentials
20 | - Content sample ==>
21 | ```json
22 | {
23 | "tenant_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
24 | "subscription_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
25 | "client_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
26 | "client_secret": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
27 | "app_admin": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
28 | "pass": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
29 | }
30 | ```
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-3/var.tf:
--------------------------------------------------------------------------------
1 | #Variables declaration
2 |
3 | variable "subscription_id" {
4 | description = "Azure subscription Id."
5 | }
6 |
7 | variable "tenant_id" {
8 | description = "Azure tenant Id."
9 | }
10 |
11 | variable "client_id" {
12 | description = "Azure service principal application Id"
13 | }
14 |
15 | variable "client_secret" {
16 | description = "Azure service principal application Secret"
17 | }
18 |
19 | variable "subnets" {
20 | description = "Subnet list."
21 | type = any
22 | }
23 |
24 | variable "Lbs" {
25 | description = "List containing your load balancers."
26 | type = any
27 | }
28 | variable "vms" {
29 | description = "VMs list."
30 | type = any
31 | }
32 |
33 | variable "windows_storage_image_reference" {
34 | type = map(string)
35 | description = "Could containt an 'id' of a custom image or the following parameters for an Azure public 'image publisher','offer','sku', 'version'"
36 | }
37 |
38 | variable "app_admin" {
39 | description = "Specifies the name of the administrator account on the VM."
40 | }
41 |
42 | variable "pass" {
43 | description = "Specifies the password of the administrator account on the VM."
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-3/variable/main-jdld.tfvars:
--------------------------------------------------------------------------------
1 | #Variables initialization
2 |
3 | subnets = {
4 | snet1 = {
5 | vnet_key = "vnet1" #(Mandatory)
6 | name = "bp3-front-snet1" #(Mandatory)
7 | address_prefix = "10.0.3.0/24" #(Mandatory)
8 | service_endpoints = ["Microsoft.Sql", "Microsoft.Storage"] #(Optional) delete this line for no Service Endpoints
9 | }
10 | }
11 |
12 | Lbs = {
13 | lb1 = {
14 | id = "1" #Id of the load balancer use as a suffix of the load balancer name
15 | suffix_name = "internal"
16 | subnet_iteration = "0" #Id of the Subnet
17 | static_ip = "10.0.3.4" #(Optional) Set null to get dynamic IP or delete this line
18 | }
19 | }
20 |
21 | vms = {
22 | vm1 = {
23 | suffix_name = "rds" #(Mandatory) suffix of the vm
24 | id = "1" #(Mandatory) Id of the VM
25 | static_ip = "10.0.3.5" #(Optional) Set null to get dynamic IP or delete this line
26 | internal_lb_iteration = "0" #(Optional) Id of the Internal Load Balancer, set to null or delete the line if there is no Load Balancer
27 | storage_data_disks = [] #(Mandatory) For no data disks set []
28 | subnet_iteration = "0" #(Mandatory) Id of the Subnet
29 | zones = ["1"] #Availability Zone id, could be 1, 2 or 3, if you don't need to set it to null or delete the line
30 | vm_size = "Standard_B2s" #(Mandatory)
31 | managed_disk_type = "Premium_LRS" #(Mandatory)
32 | }
33 |
34 | vm1 = {
35 | suffix_name = "rds" #(Mandatory) suffix of the vm
36 | id = "2" #(Mandatory) Id of the VM
37 | static_ip = "10.0.3.6" #(Optional) Set null to get dynamic IP or delete this line
38 | internal_lb_iteration = null #(Optional) Id of the Internal Load Balancer, set to null or delete the line if there is no Load Balancer
39 | storage_data_disks = [] #(Mandatory) For no data disks set []
40 | subnet_iteration = "0" #(Mandatory) Id of the Subnet
41 | zones = ["1"] #Availability Zone id, could be 1, 2 or 3, if you don't need to set it to null or delete the line
42 | vm_size = "Standard_B2s" #(Mandatory)
43 | managed_disk_type = "Premium_LRS" #(Mandatory)
44 | }
45 | }
46 |
47 | windows_storage_image_reference = {
48 | publisher = "MicrosoftWindowsServer"
49 | offer = "WindowsServer"
50 | sku = "2016-Datacenter"
51 | version = "Latest"
52 | }
53 |
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-4/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled files
2 | *.tfstate
3 | *.tfstate.backup
4 | *.DS_Store
5 |
6 | # Directory
7 | .terraform/
8 |
9 | # Secret files
10 | secret/*.tfvars
11 |
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-4/README.md:
--------------------------------------------------------------------------------
1 | [Previous page >](../)
2 |
3 | Best Practice 4
4 | ------------
5 | These are my recommandations concerning the usage of the `azurerm_template_deployment` Terraform resource :
6 | 1. Don't use the `azurerm_template_deployment` Terraform resource
7 | 2. If you don't have the choice because one Terraform resource doesn't exist
8 | * Create a feature request [here on GitHub](https://github.com/terraform-providers/terraform-provider-azurerm/issues/new/choose)
9 | * Use the `azurerm_template_deployment` Terraform resource (a demo will be shown in this article)
10 |
11 | "Terraform can only manage the deployment of the ARM Template - and not any resources which are created by it. It's highly recommended using the Native Resources where possible instead rather than an ARM Template" (see [this article](https://www.terraform.io/docs/providers/azurerm/r/template_deployment.html) from terraform.io website for more information).
12 |
13 | In this article we will perform the following action with :
14 | 1. Create a Standard Public Load Balancer with outbound rules
15 | * Microsoft associated documentation : [Azure Load balancer outbound rules overview](https://docs.microsoft.com/en-us/azure/load-balancer/load-balancer-outbound-rules-overview)
16 | * Azure Rm Template : [azuredeploy.json](https://jamesdld.github.io/AzureRm-Template/Create-AzureRmLoadBalancerOutboundRules/)
17 |
18 |
19 | ### Prerequisite
20 | -----
21 |
22 | | Item | Description |
23 | | ------------- | ------------- |
24 | | Azure Subscription | An Azure subscription id |
25 | | Resource Group | An Azure resource group is available |
26 | | Storage Account | An Azure storage account is available and is located in the upper resource group, it contains a container named `tfstate` |
27 | | Service Principal | An Azure service principal is available and has the `owner` privilege on the upper resource group |
28 | | Terraform file | [Clone this repository](https://github.com/JamesDLD/terraform/tree/master/Best-Practice/BestPractice-4) and fill in the following files with the upper prerequisite items :
Variable used for the Terraform `init` : secret/backend-jdld.json
Variable used for the Terraform `plan` and `apply` : [main.tf](main.tf) & [main-jdld.tfvars](variable/main-jdld.tfvars) & secret/main-jdld.json |
29 |
30 |
31 |
32 | What should we do?
33 | ------------
34 | We will create the upper mentioned element using remote backend (see the previous article [BestPractice-1](../BestPractice-1) for more information about remote backend).
35 |
36 | Review the [main.tf file of the module here](https://github.com/JamesDLD/terraform/tree/master/module/Add-AzureRmLoadBalancerOutboundRules).
37 | As illustrated in the following bracket that's how we can push parameters into an AzureRm template (this file template is located here : [azuredeploy.json](https://github.com/JamesDLD/AzureRm-Template/tree/master/Create-AzureRmLoadBalancerOutboundRules)
38 | ```hcl
39 | resource "azurerm_template_deployment" "lb_to_addOutboundRule" {
40 | name = "${var.lbs_out[0]["suffix_name"]}-bck-DEP"
41 | resource_group_name = var.lb_out_resource_group_name
42 | template_body = file(
43 | "${path.module}/AzureRmLoadBalancerOutboundRules_template.json",
44 | )
45 | deployment_mode = "Incremental"
46 |
47 | parameters = {
48 | lbName = "${var.lb_out_prefix}${var.lbs_out[0]["suffix_name"]}${var.lb_out_suffix}"
49 | tags = jsonencode(var.lbs_tags)
50 | sku = var.lbs_out[0]["sku"]
51 | allocatedOutboundPorts = var.lbs_out[0]["allocatedOutboundPorts"]
52 | idleTimeoutInMinutes = var.lbs_out[0]["idleTimeoutInMinutes"]
53 | enableTcpReset = var.lbs_out[0]["enableTcpReset"]
54 | protocol = var.lbs_out[0]["protocol"]
55 | lb_public_ip_id = var.lb_public_ip_id
56 | }
57 | }
58 | ```
59 |
60 | ### 1. Usage
61 | -----
62 |
63 | This step ensures that Terraform has all the prerequisites to build your template in Azure.
64 | ```hcl
65 | terraform init -backend-config="secret/backend-jdld.json" -reconfigure
66 | ```
67 |
68 | The Terraform plan command is used to create an execution plan.
69 | This step compares the requested resources to the state information saved by Terraform and then gives as an output the planned execution. Resources are not created in Azure.
70 | ```hcl
71 | terraform plan -var-file="secret/main-jdld.json" -var-file="variable/main-jdld.tfvars"
72 | ```
73 |
74 | If all is ok with the proposal you can now apply the configuration.
75 | ```hcl
76 | terraform apply -var-file="secret/main-jdld.json" -var-file="variable/main-jdld.tfvars"
77 | ```
78 |
79 | We will now destroy what we have done and you will see that our load balancer will not be deleted.
80 | ```hcl
81 | terraform destroy -var-file="secret/main-jdld.json" -var-file="variable/main-jdld.tfvars"
82 | ```
83 |
84 | ### 2. Analysis
85 | -----
86 |
87 | | Description | Screenshot |
88 | | ------------- | ------------- |
89 | | Have a look on the deployment on Azure |  |
90 | | When processing a Terraform `destroy` our deployment object is being deleted |  |
91 | | After processing a Terraform `destroy` our load balancer is still available in Azure |  |
92 |
93 |
94 | See you!
95 |
96 | JamesDLD
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-4/image/depl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JamesDLD/terraform/0c71dce4e1814ff2cf5fbeaa99fe0f6e983db9fb/Best-Practice/BestPractice-4/image/depl.png
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-4/image/destroy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JamesDLD/terraform/0c71dce4e1814ff2cf5fbeaa99fe0f6e983db9fb/Best-Practice/BestPractice-4/image/destroy.png
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-4/image/nodestroy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JamesDLD/terraform/0c71dce4e1814ff2cf5fbeaa99fe0f6e983db9fb/Best-Practice/BestPractice-4/image/nodestroy.png
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-4/main.tf:
--------------------------------------------------------------------------------
1 | #Set the terraform backend
2 | terraform {
3 | required_version = ">= 0.12.6"
4 |
5 | backend "azurerm" {
6 | storage_account_name = "infrasdbx1vpcjdld1"
7 | container_name = "tfstate"
8 | key = "BestPractice-4.tfstate"
9 | resource_group_name = "infr-jdld-noprd-rg1"
10 | }
11 | }
12 |
13 | #Set the Provider
14 | provider "azurerm" {
15 | version = "~> 2.0" #Use the last version tested through an Azure DevOps pipeline here : https://dev.azure.com/jamesdld23/vpc_lab/_build
16 | subscription_id = var.subscription_id
17 | client_id = var.client_id
18 | client_secret = var.client_secret
19 | tenant_id = var.tenant_id
20 | features {}
21 | }
22 |
23 | #Call module/resource
24 | data "azurerm_resource_group" "infr" {
25 | name = "infr-jdld-noprd-rg1"
26 | }
27 |
28 | #Action
29 | module "Add-AzureRmLoadBalancerOutboundRules-Apps" {
30 | source = "git::https://github.com/JamesDLD/terraform.git//module/Add-AzureRmLoadBalancerOutboundRules?ref=master"
31 | lbs_out = var.lbs_public
32 | lb_out_prefix = "bp4-"
33 | lb_out_suffix = "-publiclb1"
34 | lb_out_resource_group_name = data.azurerm_resource_group.infr.name
35 | lbs_tags = data.azurerm_resource_group.infr.tags
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-4/secret/README.md:
--------------------------------------------------------------------------------
1 | Folder's golden rule
2 | ------------
3 | - Store secrets
4 | - File containing password are ignored by any git push
5 |
6 |
7 | Secret files
8 | ------------
9 | - backend-jdld.json : stores the credential to write Terraform backend files
10 | - Content sample ==>
11 | ```json
12 | {
13 | "tenant_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
14 | "subscription_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
15 | "client_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
16 | "client_secret": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
17 | }
18 | ```
19 | - main-jdld.json : stores the credential to write in Infra and Apps Resource groups and store the VM credentials
20 | - Content sample ==>
21 | ```json
22 | {
23 | "tenant_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
24 | "subscription_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
25 | "client_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
26 | "client_secret": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
27 | }
28 | ```
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-4/var.tf:
--------------------------------------------------------------------------------
1 | #Variables declaration
2 |
3 | variable "subscription_id" {
4 | description = "Azure subscription Id."
5 | }
6 |
7 | variable "tenant_id" {
8 | description = "Azure tenant Id."
9 | }
10 |
11 | variable "client_id" {
12 | description = "Azure service principal application Id"
13 | }
14 |
15 | variable "client_secret" {
16 | description = "Azure service principal application Secret"
17 | }
18 |
19 | variable "lbs_public" {
20 | description = "Load balancer properties."
21 | type = list(object({
22 | suffix_name = string
23 | sku = string
24 | allocatedOutboundPorts = number #Number of SNAT ports, Load Balancer allocates SNAT ports in multiples of 8.
25 | idleTimeoutInMinutes = number #Outbound flow idle timeout. The parameter accepts a value from 4 to 66.
26 | enableTcpReset = bool #Enable TCP Reset on idle timeout.
27 | protocol = string #Transport protocol of the outbound rule.
28 | }))
29 | }
30 |
--------------------------------------------------------------------------------
/Best-Practice/BestPractice-4/variable/main-jdld.tfvars:
--------------------------------------------------------------------------------
1 | #Variables initialization
2 |
3 | lbs_public = [
4 | {
5 | suffix_name = "outbound"
6 | sku = "Standard"
7 | allocatedOutboundPorts = "8" #Number of SNAT ports, Load Balancer allocates SNAT ports in multiples of 8.
8 | idleTimeoutInMinutes = "4" #Outbound flow idle timeout. The parameter accepts a value from 4 to 66.
9 | enableTcpReset = "false" #Enable TCP Reset on idle timeout.
10 | protocol = "Tcp" #Transport protocol of the outbound rule.
11 | },
12 | ]
13 |
--------------------------------------------------------------------------------
/Best-Practice/README.md:
--------------------------------------------------------------------------------
1 | [Previous page >](../)
2 |
3 | [](https://dev.azure.com/jamesdld23/vpc_lab/_build/latest?definitionId=5&branchName=master)
4 |
5 | Content
6 | ------------
7 |
8 | Share a list of best practices and tutoriels when using Terraform on Azure.
9 |
10 | Best Practice
11 | ------------
12 |
13 | | Id | Description |
14 | | ------------- | ------------- |
15 | | [BestPractice-1](BestPractice-1) | Use remote backend on Azure |
16 | | [BestPractice-2](BestPractice-2) | Manage Terraform, Azure Rm provider and modules version |
17 | | [BestPractice-3](BestPractice-3) | Use implicit dependencies |
18 | | [BestPractice-4](BestPractice-4) | **Warning** concering the resource `azurerm_template_deployment` |
19 |
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/CreateAzureRm-Infra.yml:
--------------------------------------------------------------------------------
1 | #Multi-stage YAML pipeline.
2 | name: $(BuildDefinitionName).$(DayOfYear)$(Rev:.r)
3 |
4 | schedules:
5 | - cron: "45 5 * * 4"
6 | branches:
7 | include:
8 | - master
9 | displayName: Weekly Thursday 5h45 am UTC build
10 | always: true
11 |
12 | variables:
13 | - group: terraform_binary # variable group containing Terraform information like the Terraform version (like terraform_version)
14 | - name: vmImageName
15 | value: "ubuntu-latest"
16 | - name: backend_main_secret_file_id1 # secret file used by the following cmdlet Terraform init, plan, apply and destroy
17 | value: "backend-main-jdld-1.json"
18 | - name: main_secret_file_id2 # secret file used by the following cmdlet Terraform init, plan, apply and destroy
19 | value: "main-jdld-2.tfvars"
20 | - name: artifact_name1
21 | value: "CreateAzureRm-Infra1"
22 | - name: artifact_name2
23 | value: "CreateAzureRm-Infra2"
24 |
25 | resources:
26 | repositories:
27 | - repository: Yaml_Templates # identifier (A-Z, a-z, 0-9, and underscore)
28 | type: github
29 | endpoint: JamesDLD # name of the service connection to use (for non-Azure Repos types)
30 | name: JamesDLD/terraform
31 | #ref: refs/tags/0.0.1 # ref name to use, defaults to 'refs/heads/master'
32 |
33 | trigger:
34 | batch: true # when a build is running, the system waits until the build is completed
35 | branches:
36 | include:
37 | - master
38 | - feature/*
39 | - release/*
40 | paths:
41 | include:
42 | - CreateAzureRm-Infra/*
43 |
44 | stages:
45 | - stage: Build_Infra
46 | jobs:
47 | - job: Terraform_Plan_Infra
48 | displayName: Terraform Plan - Infra - Publish a package if Infrastructure changes are identified
49 | continueOnError: false
50 | pool:
51 | vmImage: $(vmImageName)
52 | steps:
53 | - task: DownloadSecureFile@1
54 | displayName: "Download secure file $(backend_main_secret_file_id1)"
55 | inputs:
56 | secureFile: $(backend_main_secret_file_id1)
57 |
58 | - task: DownloadSecureFile@1
59 | displayName: "Download secure file $(main_secret_file_id2)"
60 | inputs:
61 | secureFile: $(main_secret_file_id2)
62 |
63 | - template: pipeline/terraform.yml@Yaml_Templates
64 | parameters:
65 | version: $(terraform_version)
66 | path: "./CreateAzureRm-Infra/infra/"
67 | package_name: "Infra"
68 | terraform_init: true
69 | terraform_plan: true
70 | backend_file_path: "variable/infra-backend-jdld.tfvars"
71 | backend_secret_file_id: $(backend_main_secret_file_id1)
72 | main_secret_file_id: $(main_secret_file_id2)
73 | main_file_path: "variable/infra-main-jdld.tfvars"
74 |
75 | - publish: "./ArtifactPublishLocation" # Local path to include in the Artifact
76 | artifact: "$(artifact_name1)"
77 |
78 | - stage: Deploy_Infra
79 | dependsOn: Build_Infra
80 | jobs:
81 | # track deployments on the environment
82 | - deployment: Terraform_Apply_Infra
83 | displayName: Terraform Apply - Infra - Resources creation
84 | pool:
85 | vmImage: $(vmImageName)
86 | environment: "Terraform_Apply"
87 | strategy:
88 | # default deployment strategy
89 | runOnce:
90 | deploy:
91 | steps:
92 | - template: pipeline/terraform.yml@Yaml_Templates
93 | parameters:
94 | version: $(terraform_version)
95 | artifact_path: $(Pipeline.Workspace)/$(artifact_name1)
96 | package_name: "Infra"
97 | terraform_apply: true
98 |
99 | - stage: Build_Apps
100 | dependsOn: Deploy_Infra
101 | jobs:
102 | - job: Terraform_Plan_Apps
103 | displayName: Terraform Plan - Apps - Publish a package if Infrastructure changes are identified
104 | continueOnError: false
105 | pool:
106 | vmImage: $(vmImageName)
107 | steps:
108 | - task: DownloadSecureFile@1
109 | displayName: "Download secure file $(backend_main_secret_file_id1)"
110 | inputs:
111 | secureFile: $(backend_main_secret_file_id1)
112 |
113 | - task: DownloadSecureFile@1
114 | displayName: "Download secure file $(main_secret_file_id2)"
115 | inputs:
116 | secureFile: $(main_secret_file_id2)
117 |
118 | - template: pipeline/terraform.yml@Yaml_Templates
119 | parameters:
120 | version: $(terraform_version)
121 | path: "./CreateAzureRm-Infra/apps/"
122 | package_name: "Apps"
123 | terraform_init: true
124 | terraform_plan: true
125 | backend_file_path: "variable/apps-backend-jdld.tfvars"
126 | backend_secret_file_id: $(backend_main_secret_file_id1)
127 | main_secret_file_id: $(main_secret_file_id2)
128 | main_file_path: "variable/apps-main-jdld.tfvars"
129 |
130 | - publish: "./ArtifactPublishLocation" # Local path to include in the Artifact
131 | artifact: "$(artifact_name2)"
132 |
133 | - stage: Deploy_Apps
134 | dependsOn: Build_Apps
135 | jobs:
136 | # track deployments on the environment
137 | - deployment: Terraform_Apply_Apps
138 | displayName: Terraform Apply - Apps - Resources creation
139 | pool:
140 | vmImage: $(vmImageName)
141 | environment: "Terraform_Apply"
142 | strategy:
143 | # default deployment strategy
144 | runOnce:
145 | deploy:
146 | steps:
147 | - template: pipeline/terraform.yml@Yaml_Templates
148 | parameters:
149 | version: $(terraform_version)
150 | artifact_path: $(Pipeline.Workspace)/$(artifact_name2)
151 | package_name: "Apps"
152 | terraform_apply: true
153 |
154 | - stage: Deliver
155 | dependsOn: Deploy_Apps
156 | jobs:
157 | # track deployments on the environment
158 | - deployment: Terraform_Destroy
159 | displayName: Terraform Destroy - Script ok, now deleting the resources
160 | pool:
161 | vmImage: $(vmImageName)
162 | environment: "Terraform_Destroy"
163 | strategy:
164 | # default deployment strategy
165 | runOnce:
166 | deploy:
167 | steps:
168 | - task: DownloadSecureFile@1
169 | displayName: "Download secure file $(main_secret_file_id2)"
170 | inputs:
171 | secureFile: $(main_secret_file_id2)
172 |
173 | - task: DownloadSecureFile@1
174 | displayName: "Download secure file $(backend_main_secret_file_id1)"
175 | inputs:
176 | secureFile: $(backend_main_secret_file_id1)
177 |
178 | - template: pipeline/terraform.yml@Yaml_Templates
179 | parameters:
180 | version: $(terraform_version)
181 | artifact_path: $(Pipeline.Workspace)/$(artifact_name2)
182 | package_name: "Apps"
183 | terraform_destroy: true
184 | main_secret_file_id: $(main_secret_file_id2)
185 | main_file_path: "variable/apps-main-jdld.tfvars"
186 |
187 | - template: pipeline/terraform.yml@Yaml_Templates
188 | parameters:
189 | version: $(terraform_version)
190 | artifact_path: $(Pipeline.Workspace)/$(artifact_name1)
191 | package_name: "Infra"
192 | terraform_destroy: true
193 | main_secret_file_id: $(main_secret_file_id2)
194 | main_file_path: "variable/infra-main-jdld.tfvars"
195 |
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/README.md:
--------------------------------------------------------------------------------
1 | [Previous page >](../)
2 |
3 | [](https://dev.azure.com/jamesdld23/vpc_lab/_build/latest?definitionId=6&branchName=master)
4 |
5 | Azure and Terraform
6 | ------------
7 | Simple and Powerful
8 |
9 | HashiCorp Terraform enables you to safely and predictably create, change, and improve infrastructure. It is an open source tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.
10 |
11 |
12 | About the [Terraform's modules](https://registry.terraform.io/modules/JamesDLD)
13 | ------------
14 | On of the objective here is to share Terraform custom modules with the community with the following guidelines :
15 | - a module is used when we need to call a given number of resources several times and the same way, for exemple : when creating a VM we need nic, disks, backup, log monitoring, etc ..
16 | - a module doesn't contain any static values
17 | - a module is called using variables
18 |
19 | Workflow
20 | ------------
21 | 
22 |
23 | Usage
24 | -----
25 |
26 | | Step | Description |
27 | | ------------- | ------------- |
28 | | [1 - Infra](infra) | Deliver the Infra |
29 | | [2 - Apps](apps) | Deliver an Apps environment |
30 |
31 | General Requirements
32 | ------------
33 |
34 | - [Terraform](https://www.Terraform.io/downloads.html) 0.12.6
35 | - [AzureRM Terraform Provider](https://github.com/Terraform-providers/Terraform-provider-azurerm/blob/master/README.md)
36 | - [AzureRM Terraform Provider - Authentication](https://www.Terraform.io/docs/providers/azurerm/)
37 | - The called "Infra" Azure Service Principal has the following privileges :
38 | - Owner privilege at Azure Subscription level (mandatory to create custom roles)
39 | - Read directory data on Windows Azure Active Directory (mandatory to assign custom roles)
40 | - The called "Apps" Azure Service Principal has the following privilege :
41 | - Reader privilege at Azure Subscription level (mandatory to use terraform data source ressource)
42 |
43 | Golden rules
44 | ------------
45 | - Always use Terraform implicit dependency, evict the use of the depends_on argument, [see Terraform dependencies article for more info](https://www.terraform.io/intro/getting-started/dependencies.html)
46 | - Use remote backends to save your Terraform state, [see Terraform remote backends article for more info](https://www.terraform.io/intro/getting-started/remote.html)
47 |
48 | Improvment & Feature request & Limitation
49 | ------------
50 | - Even with the use of implicit dependency, the script doesn't wait enough between the privileges setting for Apps SPN and the when Apps SPN creates it's objects, for this reason the script will raise a privilege error at the first time, you will have to relaunch it to have a full success of all operations. After this, the Apps SPN doesn't need anymore to have the Reader privilege at the subscription level. [This might me solved by with feature "timeout" explained here](https://github.com/terraform-providers/terraform-provider-azurerm/issues/2807).
51 | - Terraform authentication to AzureRM via Service Principal & certificate
52 | - Feature Request: resource azurerm_automation_variable, [ticket raised here](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1312)
53 |
54 | Solved issues
55 | ------------
56 | - Use multiple Azure service principal through the provider AzureRm, [ticket raised here](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1308)
57 | - Solution : usage of provider.azurerm v1.6.0
58 | - Use condition to decide wether or not a NIC should be linked to a Load Balancer, [ticket raised here](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1318)
59 | - Solution : usage of map and the foreach feature
60 | - Terraform resource for AzureRm recovery services is now available, we can use the native resource since provider.azurerm v1.4.0. [change log is available here for info](https://github.com/terraform-providers/terraform-provider-azurerm/blob/master/CHANGELOG.md)
61 | - Set the BackupStorageRedundancy paremeter (LRS or GRS) in the RecoveryServices/vaults template, [Microsoft.RecoveryServices/vaults template reference](https://docs.microsoft.com/en-us/azure/templates/microsoft.recoveryservices/vaults)
62 | - Solution : use sku Standard, RS0, [Terraform resource azurerm_recovery_services_vault](https://www.terraform.io/docs/providers/azurerm/r/recovery_services_vault.html)
63 | - [Work still needed on Microsoft API to use RS0 (LRS) at the RSV creation](https://github.com/Azure/azure-rest-api-specs/issues/4901)
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/apps/README.md:
--------------------------------------------------------------------------------
1 | [Previous page >](../)
2 |
3 | Step 2 : Deliver an Apps environment
4 | -----
5 |
6 | With your Terraform template created, the first step is to initialize Terraform.
7 | This step ensures that Terraform has all the prerequisites to build your template in Azure.
8 |
9 | ```hcl
10 |
11 | terraform init -backend-config="variable/apps-backend-jdld.tfvars" -backend-config="../secret/backend-jdld.json" -reconfigure
12 |
13 | ```
14 |
15 | The terraform plan command is used to create an execution plan.
16 | This step compares the requested resources to the state information saved by Terraform and then gives as an output the planned execution. Resources are not created in Azure.
17 | ```hcl
18 |
19 | terraform plan -var-file="variable/apps-main-jdld.tfvars" -var-file="../secret/main-jdld.tfvars"
20 |
21 | ```
22 |
23 | If all is ok with the proposal you can now apply the configuration.
24 | ```hcl
25 |
26 | terraform apply -var-file="variable/apps-main-jdld.tfvars" -var-file="../secret/main-jdld.tfvars"
27 |
28 | ```
29 |
30 | To destroy the associated resources.
31 | ```hcl
32 |
33 | terraform destroy -var-file="variable/apps-main-jdld.tfvars" -var-file="../secret/main-jdld.tfvars"
34 |
35 | ```
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/apps/main.tf:
--------------------------------------------------------------------------------
1 | # Providers (Infra & Apps)
2 |
3 | provider "azurerm" {
4 | subscription_id = var.subscription_id
5 | client_id = var.service_principals[1]["Application_Id"]
6 | client_secret = var.service_principals[1]["Application_Secret"]
7 | tenant_id = var.tenant_id
8 | version = "~> 2.0"
9 | features {}
10 | }
11 |
12 | # Call Resources and Modules
13 |
14 | ####################################################
15 | ########## Infra ##########
16 | ####################################################
17 |
18 | ## Prerequisistes Inventory
19 | data "azurerm_resource_group" "Infr" {
20 | name = var.rg_infr_name
21 | }
22 |
23 | data "azurerm_storage_account" "Infr" {
24 | name = var.sa_infr_name
25 | resource_group_name = var.rg_infr_name
26 | }
27 |
28 | data "azurerm_recovery_services_vault" "vault" {
29 | name = var.bck_rsv_name
30 | resource_group_name = data.azurerm_resource_group.Infr.name
31 | }
32 |
33 | data "azurerm_subnet" "snets" {
34 | count = length(var.apps_snets)
35 | name = var.apps_snets[count.index]["subnet_name"]
36 | virtual_network_name = var.apps_snets[count.index]["vnet_name"]
37 | resource_group_name = data.azurerm_resource_group.Infr.name
38 | }
39 |
40 | ####################################################
41 | ########## Apps ##########
42 | ####################################################
43 |
44 | ## Prerequisistes Inventory
45 | data "azurerm_resource_group" "MyApps" {
46 | name = var.rg_apps_name
47 | }
48 |
49 | ## Core Network components
50 |
51 | resource "azurerm_network_security_group" "apps_nsgs" {
52 | for_each = var.apps_nsgs
53 | name = "${var.app_name}-${var.env_name}-nsg${each.value["id"]}"
54 | location = data.azurerm_resource_group.MyApps.location
55 | resource_group_name = data.azurerm_resource_group.MyApps.name
56 |
57 | dynamic "security_rule" {
58 | for_each = each.value["security_rules"]
59 | content {
60 | description = lookup(security_rule.value, "description", null)
61 | direction = lookup(security_rule.value, "direction", null)
62 | name = lookup(security_rule.value, "name", null)
63 | access = lookup(security_rule.value, "access", null)
64 | priority = lookup(security_rule.value, "priority", null)
65 | source_address_prefix = lookup(security_rule.value, "source_address_prefix", null)
66 | source_address_prefixes = lookup(security_rule.value, "source_address_prefixes", null)
67 | destination_address_prefix = lookup(security_rule.value, "destination_address_prefix", null)
68 | destination_address_prefixes = lookup(security_rule.value, "destination_address_prefixes", null)
69 | destination_port_range = lookup(security_rule.value, "destination_port_range", null)
70 | destination_port_ranges = lookup(security_rule.value, "destination_port_ranges", null)
71 | protocol = lookup(security_rule.value, "protocol", null)
72 | source_port_range = lookup(security_rule.value, "source_port_range", null)
73 | source_port_ranges = lookup(security_rule.value, "source_port_ranges", null)
74 | }
75 | }
76 | tags = data.azurerm_resource_group.MyApps.tags
77 | }
78 |
79 |
80 | ## Virtual Machines components
81 |
82 | module "Create-AzureRmLoadBalancer-Apps" {
83 | source = "JamesDLD/Az-LoadBalancer/azurerm"
84 | version = "0.1.1"
85 | Lbs = var.Lbs
86 | LbRules = var.LbRules
87 | lb_prefix = "${var.app_name}-${var.env_name}"
88 | lb_location = data.azurerm_resource_group.MyApps.location
89 | lb_resource_group_name = data.azurerm_resource_group.MyApps.name
90 | Lb_sku = var.Lb_sku
91 | subnets_ids = data.azurerm_subnet.snets.*.id
92 | lb_additional_tags = { iac = "Terraform" }
93 | }
94 |
95 | module "Az-Vm" {
96 | source = "JamesDLD/Az-Vm/azurerm"
97 | version = "0.1.2"
98 | sa_bootdiag_storage_uri = data.azurerm_storage_account.Infr.primary_blob_endpoint #(Mandatory)
99 | subnets_ids = data.azurerm_subnet.snets.*.id #(Mandatory)
100 | linux_vms = var.linux_vms #(Mandatory)
101 | windows_vms = var.windows_vms #(Mandatory)
102 | vm_resource_group_name = data.azurerm_resource_group.MyApps.name
103 | vm_prefix = "" #(Optional)
104 | admin_username = var.app_admin
105 | admin_password = var.pass
106 | ssh_key = var.ssh_key
107 | enable_log_analytics_dependencies = false #(Optional) Default is false
108 | recovery_services_vault_name = data.azurerm_recovery_services_vault.vault.name #(Optional)
109 | recovery_services_vault_rgname = data.azurerm_resource_group.Infr.name #(Optional) Use the RG's location if not set
110 | nsgs_ids = [for x in azurerm_network_security_group.apps_nsgs : x.id] #(Optional)
111 | internal_lb_backend_ids = module.Create-AzureRmLoadBalancer-Apps.lb_backend_ids #(Optional)
112 | vm_additional_tags = { iac = "Terraform" }
113 |
114 | #All other optional values
115 | /*
116 | key_vault_name = var.key_vault_name #(Optional)
117 | key_vault_rgname = data.azurerm_resource_group.Infr.name #(Optional) Use the RG's location if not set
118 | vm_location = element(module.Az-VirtualNetwork-Demo.vnet_locations, 0) #(Optional) Use the RG's location if not set
119 | workspace_name = azurerm_log_analytics_workspace.Apps.name #(Optional)
120 | workspace_resource_rgname = "" #(Optional) Use the RG's location if not set
121 | public_ip_ids = module.Az-VirtualNetwork-Demo.public_ip_ids #(Optional)
122 | public_lb_backend_ids = ["public_backend_id1", "public_backend_id1"] #(Optional)
123 | */
124 |
125 | }
126 | # Infra cross services for Apps
127 | #N/A
128 |
129 | ## Infra common services
130 | #N/A
131 |
132 |
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/apps/variable/apps-backend-jdld.tfvars:
--------------------------------------------------------------------------------
1 | #Variables initialization
2 | storage_account_name = "infrasdbx1vpcjdld1"
3 |
4 | container_name = "tfstate"
5 |
6 | key = "apps.tfstate"
7 |
8 | resource_group_name = "infr-jdld-noprd-rg1"
9 |
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/apps/variable/apps-main-jdld.tfvars:
--------------------------------------------------------------------------------
1 | #Variables initialization
2 |
3 | #Common variables
4 | app_name = "jdld"
5 |
6 | env_name = "apps"
7 |
8 | #Below tag variable is no more used as this data is now probed trhough the data source module "Get-AzureRmResourceGroup"
9 | default_tags = {
10 | ENV = "sand1"
11 | APP = "JDLD"
12 | BUD = "FR_BXXXXX"
13 | CTC = "j.dumont@veebaze.com"
14 | }
15 |
16 | rg_apps_name = "apps-jdld-sand1-rg1"
17 |
18 | rg_infr_name = "infr-jdld-noprd-rg1"
19 |
20 | #Storage
21 | sa_infr_name = "infrasdbx1vpcjdld1"
22 |
23 | #Backup
24 | bck_rsv_name = "jdld-infr-rsv1"
25 |
26 | #Network
27 |
28 | apps_snets = [
29 | {
30 | vnet_name = "jdld-infr-apps-vnet1"
31 | subnet_name = "front1"
32 | },
33 | {
34 | vnet_name = "jdld-infr-apps-vnet1"
35 | subnet_name = "back1"
36 | },
37 | ]
38 |
39 | apps_nsgs = {
40 |
41 | default_nsg1 = {
42 | id = "1"
43 | security_rules = [
44 | {
45 | description = "Demo1"
46 | direction = "Inbound"
47 | name = "ALL_to_NIC_tcp-3389"
48 | access = "Allow"
49 | priority = "2000"
50 | source_address_prefix = "*"
51 | destination_address_prefix = "*"
52 | destination_port_range = "3389"
53 | protocol = "tcp"
54 | source_port_range = "*"
55 | },
56 | {
57 | direction = "Inbound"
58 | name = "ALL_to_NIC_tcp-80-443"
59 | access = "Allow"
60 | priority = "2001"
61 | source_address_prefix = "*"
62 | destination_address_prefix = "*"
63 | destination_port_range = "80"
64 | protocol = "tcp"
65 | source_port_range = "*"
66 | },
67 | {
68 | direction = "Outbound"
69 | name = "ALL_to_GoogleDns_udp-53"
70 | access = "Allow"
71 | priority = "2001"
72 | source_address_prefix = "*"
73 | destination_address_prefix = "8.8.8.8"
74 | destination_port_range = "53"
75 | protocol = "*"
76 | source_port_range = "*"
77 | },
78 | ]
79 | }
80 | }
81 |
82 | # Virtual Machines components : Load Balancer & Availability Zone & Nic & VM
83 | Lb_sku = "Standard" #"Basic"
84 |
85 | Lbs = {
86 |
87 | iis1 = {
88 | id = "1" #Id of the load balancer use as a suffix of the load balancer name
89 | suffix_name = "iis"
90 | subnet_iteration = "0" #Id of the Subnet
91 | static_ip = "10.0.2.4"
92 | }
93 |
94 | rdg1 = {
95 | id = "1" #Id of the load balancer use as a suffix of the load balancer name
96 | suffix_name = "rdg"
97 | subnet_iteration = "1" #Id of the Subnet
98 | static_ip = "10.0.2.68"
99 | }
100 |
101 | }
102 |
103 | LbRules = {
104 |
105 | iis_lbrule1 = {
106 | Id = "1" #Id of a the rule within the Load Balancer
107 | lb_key = "iis1" #Id of the Load Balancer
108 | suffix_name = "iis" #MUST match the Lbs suffix_name
109 | lb_port = "80"
110 | backend_port = "80"
111 | probe_port = "80"
112 | probe_protocol = "Http"
113 | request_path = "/"
114 | load_distribution = "Default"
115 | }
116 |
117 | rdg_lbrule1 = {
118 | Id = "1" #Id of a the rule within the Load Balancer
119 | lb_key = "rdg1" #Id of the Load Balancer
120 | suffix_name = "rdg" #MUST match the Lbs suffix_name
121 | lb_port = "443"
122 | backend_port = "443"
123 | probe_port = "443"
124 | probe_protocol = "Tcp"
125 | request_path = ""
126 | load_distribution = "Default"
127 | }
128 |
129 | }
130 |
131 | windows_vms = {
132 |
133 | vm1 = {
134 | suffix_name = "iis" #(Mandatory) suffix of the vm
135 | id = "1" #(Mandatory) Id of the VM
136 | internal_lb_iteration = "0" #(Optional) Id of the Internal Load Balancer, set to null or delete the line if there is no Load Balancer
137 | storage_data_disks = [] #(Mandatory) For no data disks set []
138 | subnet_iteration = "0" #(Mandatory) Id of the Subnet
139 | security_group_iteration = "1" #(Optional) Id of the Network Security Group
140 | static_ip = "10.0.2.8" #(Optional) Set null to get dynamic IP or delete this line
141 | zones = ["1"] #Availability Zone id, could be 1, 2 or 3, if you don't need to set it to "", WARNING you could not have Availabilitysets and AvailabilityZones
142 | vm_size = "Standard_B2ms" #(Mandatory)
143 | managed_disk_type = "Premium_LRS" #(Mandatory)
144 | }
145 |
146 | vm2 = {
147 | suffix_name = "rdg" #(Mandatory) suffix of the vm
148 | id = "1" #(Mandatory) Id of the VM
149 | internal_lb_iteration = "1" #(Optional) Id of the Internal Load Balancer, set to null or delete the line if there is no Load Balancer
150 | storage_data_disks = [] #(Mandatory) For no data disks set []
151 | subnet_iteration = "1" #(Mandatory) Id of the Subnet
152 | security_group_iteration = "1" #(Optional) Id of the Network Security Group
153 | static_ip = "10.0.2.72" #(Optional) Set null to get dynamic IP or delete this line
154 | zones = ["2"] #Availability Zone id, could be 1, 2 or 3, if you don't need to set it to "", WARNING you could not have Availabilitysets and AvailabilityZones
155 | vm_size = "Standard_B2ms" #(Mandatory)
156 | managed_disk_type = "Premium_LRS" #(Mandatory)
157 | #backup_policy_name = "BackupPolicy-Schedule1" #(Optional) Set null to disable backup (WARNING, this will delete previous backup) otherwise set a backup policy like BackupPolicy-Schedule1
158 | }
159 |
160 | }
161 |
162 | linux_vms = {
163 |
164 | vm1 = {
165 | suffix_name = "ssh" #(Mandatory) suffix of the vm
166 | id = "1" #(Mandatory) Id of the VM
167 | storage_data_disks = [
168 | {
169 | id = "1" #Disk id
170 | lun = "0"
171 | disk_size_gb = "32"
172 | managed_disk_type = "Premium_LRS"
173 | caching = "ReadWrite"
174 | create_option = "Empty"
175 | },
176 | ] #(Mandatory) For no data disks set []
177 | subnet_iteration = "0" #(Mandatory) Id of the Subnet
178 | security_group_iteration = "1" #(Optional) Id of the Network Security Group
179 | static_ip = "10.0.2.9" #(Optional) Set null to get dynamic IP or delete this line
180 | zones = ["1"] #Availability Zone id, could be 1, 2 or 3, if you don't need to set it to "", WARNING you could not have Availabilitysets and AvailabilityZones
181 | #backup_policy_name = "BackupPolicy-Schedule1" #(Optional) Set null to disable backup (WARNING, this will delete previous backup) otherwise set a backup policy like BackupPolicy-Schedule1
182 | vm_size = "Standard_B2ms" #(Mandatory)
183 | managed_disk_type = "Premium_LRS" #(Mandatory)
184 | }
185 |
186 | vm2 = {
187 | suffix_name = "ssh" #(Mandatory) suffix of the vm
188 | id = "2" #(Mandatory) Id of the VM
189 | storage_data_disks = [] #(Mandatory) For no data disks set []
190 | subnet_iteration = "1" #(Mandatory) Id of the Subnet
191 | security_group_iteration = "1" #(Optional) Id of the Network Security Group
192 | static_ip = "10.0.2.73" #(Optional) Set null to get dynamic IP or delete this line
193 | zones = ["2"] #Availability Zone id, could be 1, 2 or 3, if you don't need to set it to "", WARNING you could not have Availabilitysets and AvailabilityZones
194 | vm_size = "Standard_B2ms" #(Mandatory)
195 | managed_disk_type = "Premium_LRS" #(Mandatory)
196 | }
197 |
198 | }
199 |
200 | ## Infra common services
201 | #N/A
202 |
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/apps/variables.tf:
--------------------------------------------------------------------------------
1 | #Variables declaration
2 |
3 | #Authentication
4 | terraform {
5 | backend "azurerm" {
6 | }
7 | required_version = ">= 0.12.6"
8 | }
9 |
10 | variable "subscription_id" {
11 | description = "Azure subscription Id."
12 | type = string
13 | }
14 |
15 | variable "tenant_id" {
16 | description = "Azure tenant Id."
17 | type = string
18 | }
19 |
20 | variable "service_principals" {
21 | type = list
22 | description = "Azure service principals list containing the following keys : Application_Name, Application_Id, Application_Secret, Application_object_id."
23 | }
24 |
25 | #Common
26 | variable "app_name" {
27 | description = "Application name used in objects naming convention."
28 | type = string
29 | }
30 |
31 | variable "env_name" {
32 | description = "Environment name used in objects naming convention."
33 | type = string
34 | }
35 |
36 | variable "default_tags" {
37 | type = map(string)
38 | description = "Tag map that will be pushed on all Azure resources."
39 | }
40 |
41 | variable "rg_apps_name" {
42 | description = "Apps resource group name."
43 | type = string
44 | }
45 |
46 | variable "rg_infr_name" {
47 | description = "infra resource group name."
48 | type = string
49 | }
50 |
51 | variable "sa_infr_name" {
52 | description = "Infra storage account name."
53 | type = string
54 | }
55 |
56 | variable "bck_rsv_name" {
57 | description = "Infra recovery services vault name."
58 | type = string
59 | }
60 |
61 | #Subnet & Network Security group
62 |
63 | variable "apps_snets" {
64 | description = "Subnets properties."
65 | type = list(object({
66 | vnet_name = string
67 | subnet_name = string
68 | }))
69 | }
70 |
71 | variable "apps_nsgs" {
72 | type = any
73 | description = "Apps Network Security Groups list containing the following keys : suffix_name."
74 | }
75 |
76 | #Load Balancers & Availability Set & Virtual Machines
77 | variable "linux_vms" {
78 | description = "Linux VMs list."
79 | type = any
80 | }
81 |
82 | variable "windows_vms" {
83 | description = "Windows VMs list."
84 | type = any
85 | }
86 | variable "Lb_sku" {
87 | description = "The SKU of the Azure Load Balancer. Accepted values are Basic and Standard. Defaults to Basic."
88 | type = string
89 | }
90 |
91 | variable "Lbs" {
92 | type = any
93 | description = "Load Balancer list containing the following keys : suffix_name, subnet_iteration, static_ip."
94 | }
95 |
96 | variable "LbRules" {
97 | type = any
98 | description = "Load Balancer rules list."
99 | }
100 |
101 | variable "app_admin" {
102 | description = "Specifies the name of the administrator account on the VM."
103 | type = string
104 | }
105 |
106 | variable "pass" {
107 | description = "Specifies the password of the administrator account on the VM."
108 | type = string
109 | }
110 |
111 | variable "ssh_key" {
112 | description = "Specifies the ssh public key to login on Linux VM."
113 | type = string
114 | }
115 |
116 | variable "key_vaults" {
117 | description = "List containing your key vaults."
118 | type = list(object({
119 | suffix_name = string
120 | policy1_tenant_id = string
121 | policy1_object_id = string
122 | policy1_application_id = string
123 | }))
124 | }
125 |
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/infra/README.md:
--------------------------------------------------------------------------------
1 | [Previous page >](../)
2 |
3 | Step 1 : Deliver the Infra
4 | -----
5 | The following sample will launch all the modules to show the reader how they are called.
6 | My advice is that the reader pick up the module he wants and calls it how it's shown in the root "main.tf" file.
7 |
8 | [Clone this repository](https://github.com/JamesDLD/terraform/tree/master/CreateAzureRm-Infra)
9 |
10 | With your Terraform template created, the first step is to initialize Terraform.
11 | This step ensures that Terraform has all the prerequisites to build your template in Azure.
12 |
13 | ```hcl
14 |
15 | terraform init -backend-config="variable/infra-backend-jdld.tfvars" -backend-config="../secret/backend-jdld.json" -reconfigure
16 |
17 | ```
18 |
19 | The terraform plan command is used to create an execution plan.
20 | This step compares the requested resources to the state information saved by Terraform and then gives as an output the planned execution. Resources are not created in Azure.
21 | ```hcl
22 |
23 | terraform plan -var-file="variable/infra-main-jdld.tfvars" -var-file="../secret/main-jdld.tfvars"
24 |
25 | ```
26 |
27 | If all is ok with the proposal you can now apply the configuration.
28 | ```hcl
29 |
30 | terraform apply -var-file="variable/infra-main-jdld.tfvars" -var-file="../secret/main-jdld.tfvars"
31 |
32 | ```
33 |
34 | To destroy the associated resources.
35 | ```hcl
36 |
37 | terraform destroy -var-file="variable/infra-main-jdld.tfvars" -var-file="../secret/main-jdld.tfvars"
38 |
39 | ```
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/infra/application_gateway.tf:
--------------------------------------------------------------------------------
1 |
2 | resource "azurerm_public_ip" "infra" {
3 | name = "appgateway1-pip1"
4 | resource_group_name = data.azurerm_resource_group.Infr.name
5 | location = data.azurerm_resource_group.Infr.location
6 | allocation_method = "Static" #Needed for app gw v2
7 | sku = "Standard" #Needed for app gw v2
8 | }
9 |
10 | # since these variables are re-used - a locals block makes this more maintainable
11 | locals {
12 | backend_address_pool_name = "infra-beap"
13 | frontend_port_name = "infra-feport"
14 | frontend_ip_configuration_name = "infra-feip"
15 | http_setting_name = "infra-be-htst"
16 | listener_name = "infra-httplstn"
17 | request_routing_rule_name = "infra-rqrt"
18 | redirect_configuration_name = "infra-rdrcfg"
19 | }
20 |
21 | resource "azurerm_application_gateway" "network" {
22 | name = "appgateway1"
23 | resource_group_name = data.azurerm_resource_group.Infr.name
24 | location = data.azurerm_resource_group.Infr.location
25 |
26 | sku {
27 | name = "Standard_v2"
28 | tier = "Standard_v2"
29 | capacity = 1
30 | }
31 |
32 | gateway_ip_configuration {
33 | name = "appgateway1-CFG"
34 | subnet_id = lookup(module.Az-VirtualNetwork-Infra.subnets, "ApplicationGatewatey1", null)["id"]
35 | }
36 |
37 | frontend_port {
38 | name = "${local.frontend_port_name}"
39 | port = 80
40 | }
41 |
42 | frontend_ip_configuration {
43 | name = "${local.frontend_ip_configuration_name}"
44 | public_ip_address_id = "${azurerm_public_ip.infra.id}"
45 | }
46 |
47 | backend_address_pool {
48 | name = "${local.backend_address_pool_name}"
49 | ip_addresses = ["10.0.2.8"]
50 | }
51 |
52 | backend_http_settings {
53 | name = "${local.http_setting_name}"
54 | cookie_based_affinity = "Disabled"
55 | path = "/"
56 | port = 80
57 | protocol = "Http"
58 | request_timeout = 1
59 | }
60 |
61 | http_listener {
62 | name = "${local.listener_name}"
63 | frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}"
64 | frontend_port_name = "${local.frontend_port_name}"
65 | protocol = "Http"
66 | }
67 |
68 | request_routing_rule {
69 | name = "${local.request_routing_rule_name}"
70 | rule_type = "Basic"
71 | http_listener_name = "${local.listener_name}"
72 | backend_address_pool_name = "${local.backend_address_pool_name}"
73 | backend_http_settings_name = "${local.http_setting_name}"
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/infra/variable/infra-backend-jdld.tfvars:
--------------------------------------------------------------------------------
1 | #Variables initialization
2 | storage_account_name = "infrasdbx1vpcjdld1"
3 |
4 | container_name = "tfstate"
5 |
6 | key = "infra.tfstate"
7 |
8 | resource_group_name = "infr-jdld-noprd-rg1"
9 |
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/infra/variables.tf:
--------------------------------------------------------------------------------
1 | #Variables declaration
2 |
3 | #Authentication
4 | terraform {
5 | backend "azurerm" {
6 | }
7 | required_version = ">= 0.12.6"
8 | }
9 |
10 |
11 | variable "subscription_id" {
12 | description = "Azure subscription Id."
13 | type = string
14 | }
15 |
16 | variable "tenant_id" {
17 | description = "Azure tenant Id."
18 | type = string
19 | }
20 |
21 | variable "service_principals" {
22 | description = "Azure service principals list."
23 | type = list(object({
24 | Application_Name = string
25 | Application_Id = string
26 | Application_Secret = string
27 | Application_object_id = string
28 | }))
29 | }
30 |
31 | #Common
32 | variable "app_name" {
33 | description = "Application name used in objects naming convention."
34 | type = string
35 | }
36 |
37 | variable "env_name" {
38 | description = "Environment name used in objects naming convention."
39 | type = string
40 | }
41 |
42 | variable "default_tags" {
43 | type = map(string)
44 | description = "Tag map that will be pushed on all Azure resources."
45 | }
46 |
47 | variable "rg_apps_name" {
48 | description = "Apps resource group name."
49 | type = string
50 | }
51 |
52 | variable "rg_infr_name" {
53 | description = "infra resource group name."
54 | type = string
55 | }
56 |
57 | variable "sa_infr_name" {
58 | description = "Infra storage account name."
59 | type = string
60 | }
61 |
62 | variable "kv_sku" {
63 | description = "Key vault sku."
64 | type = string
65 | }
66 |
67 | variable "key_vaults" {
68 | description = "List containing your key vaults."
69 | type = list(object({
70 | suffix_name = string
71 | policy1_tenant_id = string
72 | policy1_object_id = string
73 | policy1_application_id = string
74 | }))
75 | }
76 |
77 | variable "policies" {
78 | description = "Policies."
79 | type = list(object({
80 | suffix_name = string #Used to name the policy and to call json template files located into the module's folder
81 | policy_type = string
82 | mode = string
83 | }))
84 | }
85 |
86 | variable "roles" {
87 | description = "Roles list."
88 | type = list(object({
89 | suffix_name = string
90 | actions = string
91 | not_actions = string
92 | }))
93 | }
94 |
95 | #Backup
96 | variable "backup_policies" {
97 | description = "Recovery services vault backup policies."
98 | type = list(object({
99 | Name = string
100 | scheduleRunFrequency = string
101 | scheduleRunDays = string
102 | scheduleRunTimes = string
103 | timeZone = string
104 | dailyRetentionDurationCount = number
105 | weeklyRetentionDurationCount = number
106 | monthlyRetentionDurationCount = number
107 | }))
108 | }
109 |
110 | #Azure Firewall
111 | variable "az_firewall_rules" {
112 | description = "Azure firewall rules."
113 | }
114 |
115 |
116 | #Vnet & Subnet & Network Security group
117 | variable "vnets" {
118 | description = "Virtual Networks list."
119 | type = any
120 | }
121 |
122 | variable "snets" {
123 | description = "Subnet list."
124 | type = any
125 | }
126 |
127 | variable "vnets_to_peer" {
128 | description = "Vnets to peer."
129 | type = any
130 | }
131 |
132 | variable "route_tables" {
133 | description = "Route table."
134 | type = any
135 | }
136 |
137 | variable "infra_nsgs" {
138 | type = any
139 | description = "Infra Network Security Groups list containing the following keys : suffix_name."
140 | }
141 |
142 | #Compute
143 | variable "app_admin" {
144 | description = "Specifies the name of the administrator account on the VM."
145 | type = string
146 | }
147 |
148 | variable "pass" {
149 | description = "Specifies the password of the administrator account on the VM."
150 | type = string
151 | }
152 |
153 | variable "ssh_key" {
154 | description = "Specifies the ssh public key to login on Linux VM."
155 | type = string
156 | }
157 |
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/secret/README.md:
--------------------------------------------------------------------------------
1 | Folder's golden rule
2 | ------------
3 | - Store secrets
4 | - File containing password are ignored by any git push
5 |
6 |
7 | Secret files
8 | ------------
9 | - backend-jdld.json : stores the credential to write Terraform backend files
10 | - Content sample ==>
11 | ```json
12 | {
13 | "tenant_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
14 | "subscription_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
15 | "client_id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
16 | "client_secret": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
17 | }
18 | ```
19 | - main-jdld.tfvars : stores the credential to write in Infra and Apps Resource groups and store the VM credentials
20 | - Content sample ==>
21 | ```hcl
22 | subscription_id = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
23 | service_principals = [
24 | {
25 | Application_Name = "sp_infra" #Subscription Owner & Read directory data on Windows Azure Active Directory
26 | Application_Id = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
27 | Application_Secret = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
28 | Application_object_id = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" #To get this param : $(Get-AzureRmADServicePrincipal -DisplayName $Application_Name).Id
29 | },
30 | {
31 | Application_Name = "sp_apps" #No Privileges needed, will be set by the script
32 | Application_Id = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
33 | Application_Secret = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
34 | Application_object_id = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" #To get this param : $(Get-AzureRmADServicePrincipal -DisplayName $Application_Name).Id
35 | },
36 | ]
37 | tenant_id = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
38 |
39 | #VM Credential and public key certificate
40 | app_admin = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
41 | pass = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
42 | ssh_key = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
43 |
44 | #Key Vault
45 | key_vaults = [
46 | {
47 | suffix_name = "sci"
48 | policy1_tenant_id = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
49 | policy1_object_id = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
50 | policy1_application_id = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
51 | },
52 | ]
53 | ```
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/tools/bash_functions.sh:
--------------------------------------------------------------------------------
1 | #Color
2 | Correct='\033[0;32m' # Green
3 | Warning='\033[0;33m' # Jaune
4 | Error='\033[0;31m' # Red
5 | NoColor='\033[0m'
6 |
7 | function action_cmdlet {
8 | action="$1"
9 | cmdlet="$2"
10 | notanerror="$3"
11 | if [[ $notanerror = "" ]]; then notanerror="impossibletextthatyouwillneverfoundasanerror" ; fi
12 |
13 | echo -e "${Correct}$action...${NoColor}"
14 | action_cmdlet_output=$($cmdlet 2> /tmp/stderr_$$)
15 | if [[ $? != 0 ]]; then
16 | if [[ $(cat /tmp/stderr_$$) =~ $notanerror ]]; then
17 | echo -e "${Warning}You have decided that the following message is not an error : $(cat /tmp/stderr_$$) ${NoColor}"
18 | else
19 | echo -e "${Error}Error with action : $action...${NoColor}"
20 | echo -e "${Error}Error : $(cat /tmp/stderr_$$)...${NoColor}"
21 | exit 1
22 | fi
23 | fi
24 | return 0
25 | }
26 |
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/tools/mustache.min.js:
--------------------------------------------------------------------------------
1 | (function defineMustache(global, factory) { if (typeof exports === "object" && exports && typeof exports.nodeName !== "string") { factory(exports) } else if (typeof define === "function" && define.amd) { define(["exports"], factory) } else { global.Mustache = {}; factory(global.Mustache) } })(this, function mustacheFactory(mustache) { var objectToString = Object.prototype.toString; var isArray = Array.isArray || function isArrayPolyfill(object) { return objectToString.call(object) === "[object Array]" }; function isFunction(object) { return typeof object === "function" } function typeStr(obj) { return isArray(obj) ? "array" : typeof obj } function escapeRegExp(string) { return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&") } function hasProperty(obj, propName) { return obj != null && typeof obj === "object" && propName in obj } var regExpTest = RegExp.prototype.test; function testRegExp(re, string) { return regExpTest.call(re, string) } var nonSpaceRe = /\S/; function isWhitespace(string) { return !testRegExp(nonSpaceRe, string) } var entityMap = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", "/": "/", "`": "`", "=": "=" }; function escapeHtml(string) { return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap(s) { return entityMap[s] }) } var whiteRe = /\s*/; var spaceRe = /\s+/; var equalsRe = /\s*=/; var curlyRe = /\s*\}/; var tagRe = /#|\^|\/|>|\{|&|=|!/; function parseTemplate(template, tags) { if (!template) return []; var sections = []; var tokens = []; var spaces = []; var hasTag = false; var nonSpace = false; function stripSpace() { if (hasTag && !nonSpace) { while (spaces.length) delete tokens[spaces.pop()] } else { spaces = [] } hasTag = false; nonSpace = false } var openingTagRe, closingTagRe, closingCurlyRe; function compileTags(tagsToCompile) { if (typeof tagsToCompile === "string") tagsToCompile = tagsToCompile.split(spaceRe, 2); if (!isArray(tagsToCompile) || tagsToCompile.length !== 2) throw new Error("Invalid tags: " + tagsToCompile); openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + "\\s*"); closingTagRe = new RegExp("\\s*" + escapeRegExp(tagsToCompile[1])); closingCurlyRe = new RegExp("\\s*" + escapeRegExp("}" + tagsToCompile[1])) } compileTags(tags || mustache.tags); var scanner = new Scanner(template); var start, type, value, chr, token, openSection; while (!scanner.eos()) { start = scanner.pos; value = scanner.scanUntil(openingTagRe); if (value) { for (var i = 0, valueLength = value.length; i < valueLength; ++i) { chr = value.charAt(i); if (isWhitespace(chr)) { spaces.push(tokens.length) } else { nonSpace = true } tokens.push(["text", chr, start, start + 1]); start += 1; if (chr === "\n") stripSpace() } } if (!scanner.scan(openingTagRe)) break; hasTag = true; type = scanner.scan(tagRe) || "name"; scanner.scan(whiteRe); if (type === "=") { value = scanner.scanUntil(equalsRe); scanner.scan(equalsRe); scanner.scanUntil(closingTagRe) } else if (type === "{") { value = scanner.scanUntil(closingCurlyRe); scanner.scan(curlyRe); scanner.scanUntil(closingTagRe); type = "&" } else { value = scanner.scanUntil(closingTagRe) } if (!scanner.scan(closingTagRe)) throw new Error("Unclosed tag at " + scanner.pos); token = [type, value, start, scanner.pos]; tokens.push(token); if (type === "#" || type === "^") { sections.push(token) } else if (type === "/") { openSection = sections.pop(); if (!openSection) throw new Error('Unopened section "' + value + '" at ' + start); if (openSection[1] !== value) throw new Error('Unclosed section "' + openSection[1] + '" at ' + start) } else if (type === "name" || type === "{" || type === "&") { nonSpace = true } else if (type === "=") { compileTags(value) } } openSection = sections.pop(); if (openSection) throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos); return nestTokens(squashTokens(tokens)) } function squashTokens(tokens) { var squashedTokens = []; var token, lastToken; for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { token = tokens[i]; if (token) { if (token[0] === "text" && lastToken && lastToken[0] === "text") { lastToken[1] += token[1]; lastToken[3] = token[3] } else { squashedTokens.push(token); lastToken = token } } } return squashedTokens } function nestTokens(tokens) { var nestedTokens = []; var collector = nestedTokens; var sections = []; var token, section; for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { token = tokens[i]; switch (token[0]) { case "#": case "^": collector.push(token); sections.push(token); collector = token[4] = []; break; case "/": section = sections.pop(); section[5] = token[2]; collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens; break; default: collector.push(token) } } return nestedTokens } function Scanner(string) { this.string = string; this.tail = string; this.pos = 0 } Scanner.prototype.eos = function eos() { return this.tail === "" }; Scanner.prototype.scan = function scan(re) { var match = this.tail.match(re); if (!match || match.index !== 0) return ""; var string = match[0]; this.tail = this.tail.substring(string.length); this.pos += string.length; return string }; Scanner.prototype.scanUntil = function scanUntil(re) { var index = this.tail.search(re), match; switch (index) { case -1: match = this.tail; this.tail = ""; break; case 0: match = ""; break; default: match = this.tail.substring(0, index); this.tail = this.tail.substring(index) }this.pos += match.length; return match }; function Context(view, parentContext) { this.view = view; this.cache = { ".": this.view }; this.parent = parentContext } Context.prototype.push = function push(view) { return new Context(view, this) }; Context.prototype.lookup = function lookup(name) { var cache = this.cache; var value; if (cache.hasOwnProperty(name)) { value = cache[name] } else { var context = this, names, index, lookupHit = false; while (context) { if (name.indexOf(".") > 0) { value = context.view; names = name.split("."); index = 0; while (value != null && index < names.length) { if (index === names.length - 1) lookupHit = hasProperty(value, names[index]); value = value[names[index++]] } } else { value = context.view[name]; lookupHit = hasProperty(context.view, name) } if (lookupHit) break; context = context.parent } cache[name] = value } if (isFunction(value)) value = value.call(this.view); return value }; function Writer() { this.cache = {} } Writer.prototype.clearCache = function clearCache() { this.cache = {} }; Writer.prototype.parse = function parse(template, tags) { var cache = this.cache; var tokens = cache[template]; if (tokens == null) tokens = cache[template] = parseTemplate(template, tags); return tokens }; Writer.prototype.render = function render(template, view, partials) { var tokens = this.parse(template); var context = view instanceof Context ? view : new Context(view); return this.renderTokens(tokens, context, partials, template) }; Writer.prototype.renderTokens = function renderTokens(tokens, context, partials, originalTemplate) { var buffer = ""; var token, symbol, value; for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { value = undefined; token = tokens[i]; symbol = token[0]; if (symbol === "#") value = this.renderSection(token, context, partials, originalTemplate); else if (symbol === "^") value = this.renderInverted(token, context, partials, originalTemplate); else if (symbol === ">") value = this.renderPartial(token, context, partials, originalTemplate); else if (symbol === "&") value = this.unescapedValue(token, context); else if (symbol === "name") value = this.escapedValue(token, context); else if (symbol === "text") value = this.rawValue(token); if (value !== undefined) buffer += value } return buffer }; Writer.prototype.renderSection = function renderSection(token, context, partials, originalTemplate) { var self = this; var buffer = ""; var value = context.lookup(token[1]); function subRender(template) { return self.render(template, context, partials) } if (!value) return; if (isArray(value)) { for (var j = 0, valueLength = value.length; j < valueLength; ++j) { buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate) } } else if (typeof value === "object" || typeof value === "string" || typeof value === "number") { buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate) } else if (isFunction(value)) { if (typeof originalTemplate !== "string") throw new Error("Cannot use higher-order sections without the original template"); value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender); if (value != null) buffer += value } else { buffer += this.renderTokens(token[4], context, partials, originalTemplate) } return buffer }; Writer.prototype.renderInverted = function renderInverted(token, context, partials, originalTemplate) { var value = context.lookup(token[1]); if (!value || isArray(value) && value.length === 0) return this.renderTokens(token[4], context, partials, originalTemplate) }; Writer.prototype.renderPartial = function renderPartial(token, context, partials) { if (!partials) return; var value = isFunction(partials) ? partials(token[1]) : partials[token[1]]; if (value != null) return this.renderTokens(this.parse(value), context, partials, value) }; Writer.prototype.unescapedValue = function unescapedValue(token, context) { var value = context.lookup(token[1]); if (value != null) return value }; Writer.prototype.escapedValue = function escapedValue(token, context) { var value = context.lookup(token[1]); if (value != null) return mustache.escape(value) }; Writer.prototype.rawValue = function rawValue(token) { return token[1] }; mustache.name = "mustache.js"; mustache.version = "2.3.0"; mustache.tags = ["{{", "}}"]; var defaultWriter = new Writer; mustache.clearCache = function clearCache() { return defaultWriter.clearCache() }; mustache.parse = function parse(template, tags) { return defaultWriter.parse(template, tags) }; mustache.render = function render(template, view, partials) { if (typeof template !== "string") { throw new TypeError('Invalid template! Template should be a "string" ' + 'but "' + typeStr(template) + '" was given as the first ' + "argument for mustache#render(template, view, partials)") } return defaultWriter.render(template, view, partials) }; mustache.to_html = function to_html(template, view, partials, send) { var result = mustache.render(template, view, partials); if (isFunction(send)) { send(result) } else { return result } }; mustache.escape = escapeHtml; mustache.Scanner = Scanner; mustache.Context = Context; mustache.Writer = Writer; return mustache });
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/tools/process_template.handlebar.js:
--------------------------------------------------------------------------------
1 | //const mustache = require('./mustache.min.js');
2 | const mustache = require('./handlebars');
3 | const fs = require('fs');
4 |
5 | let args = process.argv.slice(2);
6 |
7 | let template_file = args[0];
8 | let json_ressource_file = args[1];
9 | let dest = args[2];
10 |
11 | if (!dest) {
12 | console.log('node process_template.js template_file ressource_file dest_file');
13 | return;
14 | }
15 |
16 |
17 | if (!fs.existsSync(template_file)) {
18 | console.error('file ' + template_file + ' does not exist');
19 | return;
20 | }
21 |
22 | if (!fs.existsSync(json_ressource_file)) {
23 | console.error('file ' + json_ressource_file + ' does not exist');
24 | return;
25 | }
26 |
27 |
28 |
29 |
30 | let template = fs.readFileSync(template_file, { encoding: "utf8" });
31 | let json_ressource = JSON.parse(fs.readFileSync(json_ressource_file, { encoding: "utf8" }));
32 |
33 | Object.assign(json_ressource, { template_warning: "Autogenerated file, see mustache folder if you want to overwrite this file" });
34 |
35 | let handletemplate = mustache.compile(template);
36 | let res = handletemplate(json_ressource);
37 |
38 | fs.writeFileSync(dest, res, { encoding: "utf8" });
39 |
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/tools/process_template.js:
--------------------------------------------------------------------------------
1 | const mustache = require('./mustache.min.js');
2 | const fs = require('fs');
3 |
4 | let args = process.argv.slice(2);
5 |
6 | let template_file = args[0];
7 | let json_ressource_file = args[1];
8 | let dest = args[2];
9 |
10 | if (!dest) {
11 | console.log('node process_template.js template_file ressource_file dest_file');
12 | return;
13 | }
14 |
15 |
16 | if (!fs.existsSync(template_file)) {
17 | console.error('file ' + template_file + ' does not exist');
18 | return;
19 | }
20 |
21 | if (!fs.existsSync(json_ressource_file)) {
22 | console.error('file ' + json_ressource_file + ' does not exist');
23 | return;
24 | }
25 |
26 |
27 |
28 |
29 | let template = fs.readFileSync(template_file, { encoding: "utf8" });
30 | let json_ressource = JSON.parse(fs.readFileSync(json_ressource_file, { encoding: "utf8" }));
31 |
32 | Object.assign(json_ressource, { template_warning: "Autogenerated file, see mustache folder if you want to overwrite this file" });
33 |
34 | let res = mustache.render(template, json_ressource);
35 |
36 | fs.writeFileSync(dest, res, { encoding: "utf8" });
37 |
--------------------------------------------------------------------------------
/CreateAzureRm-Infra/workflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JamesDLD/terraform/0c71dce4e1814ff2cf5fbeaa99fe0f6e983db9fb/CreateAzureRm-Infra/workflow.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Repository inventory
2 | ------------
3 |
4 | | Id | Description | Build Status |
5 | | ------------- | ------------- | ------------- |
6 | | [Application Gateway](azurerm_application_gateway) | Tutoriel on [medium.com](https://medium.com/faun/build-an-azure-application-gateway-with-terraform-8264fbd5fa42/?WT.mc_id=AZ-MVP-5003548) on how to build an Azure Application Gateway | N/A |
7 | | [Best-Practice](Best-Practice) | Share a list of best practices and tutoriels when using Terraform on Azure | [](https://dev.azure.com/jamesdld23/vpc_lab/_build/latest?definitionId=5&branchName=master) |
8 | | [Azure DevOps - Intro](AzureDevops-Introduction) | Share articles about CI/CD, Azure DevOps and Terraform on Azure. | [](https://dev.azure.com/jamesdld23/vpc_lab/_build/latest?definitionId=9&branchName=master) |
9 | | [CreateAzureRm-Infra](CreateAzureRm-Infra) | Share Terraform script that reveal how to create a VPC in Azure and how application client can create their resources | [](https://dev.azure.com/jamesdld23/vpc_lab/_build/latest?definitionId=6&branchName=master) |
10 |
11 |
12 | Azure and Terraform
13 | ------------
14 | Simple and Powerful
15 |
16 | HashiCorp Terraform enables you to safely and predictably create, change, and improve infrastructure. It is an open source tool that codifies APIs into declarative configuration files that can be shared among team members, treated as code, edited, reviewed, and versioned.
17 |
18 | The following table is a quick comparison feedback between Terraform and Azure ARM template.
19 |
20 | | Comparison | Terraform | ARM Template |
21 | | ------------- | ------------- | ------------- |
22 | | Pro | Common language to deal with several providers (Azure including AzureRm and Azure AD, AWS, Nutanix, VMware, Docker,...)
Detect if a resource's parameter could be updated in place or if the resources need to be re created
Compliant test could be done easily to ensure that what you have deployed remains coherent
Facilitating CICD testing as the "plan" function tells you exactly what need to be done
If the Terraform resource doesn't exist we can execute ARM template from the Terraform resource "azurerm_template_deployment" | Microsoft Azure ownership
Detect if a resource's parameter could be updated in place or if the resources need to be re created through the [What-if](https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/template-deploy-what-if?tabs=azure-powershell) feature
Compliant test could be done easily to ensure that what you have deployed remains coherent through the [What-if](https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/template-deploy-what-if?tabs=azure-powershell) feature
Facilitating CICD testing as the the [What-if](https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/template-deploy-what-if?tabs=azure-powershell) feature tells you exactly what need to be done
If the ARM template could not be used, we can launch a [Deployment Scripts](https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/deployment-script-template?tabs=CLI) from an ARM template
Variety of parameters types
Deployment log stored in the Azure Resource Group |
23 | | Cons | Could not use secure object as parameter
New release might not be delivered as fast if it was the provider own tool | AzureRm only, we can mitigate this cons using [Deployment Scripts](https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/deployment-script-template?tabs=CLI)
The deployment mode "complete" permits to guarantee that your RG contains exactly what you want but the ARM template could be hard to read depending on the number of resources you put on it |
24 |
25 |
26 | About the [Terraform's modules](https://registry.terraform.io/modules/JamesDLD)
27 | ------------
28 | On of the objective here is to share Terraform custom modules with the community with the following guidelines :
29 | - a module is used when we need to call a given number of resources several times and the same way, for exemple : when creating a VM we need nic, disks, backup, log monitoring, etc ..
30 | - a module doesn't contain any static values
31 | - a module is called using variables
32 |
33 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
2 | google_analytics: UA-129401405-1
3 |
--------------------------------------------------------------------------------
/_includes/google-analytics.html:
--------------------------------------------------------------------------------
1 | ga('create', '{{ site.google_analytics }}', 'auto');
--------------------------------------------------------------------------------
/azurerm_application_gateway/README.md:
--------------------------------------------------------------------------------
1 | [Previous page >](../)
2 |
3 | Content
4 | ------------
5 |
6 | Tutoriel on [medium.com](https://medium.com/faun/build-an-azure-application-gateway-with-terraform-8264fbd5fa42/?WT.mc_id=AZ-MVP-5003548) on how to build an Azure Application Gateway with:
7 | - A Monitoring Dashboard hosted on a [Log Analytics Workspace](https://docs.microsoft.com/en-us/azure/azure-monitor/insights/azure-networking-analytics?WT.mc_id=AZ-MVP-5003548).
8 | - A [Key Vault](https://docs.microsoft.com/en-us/azure/application-gateway/key-vault-certs?WT.mc_id=AZ-MVP-5003548) as a safeguard of our Web TLS/SSL certificates.
9 |
10 |
11 | Learn how to Use Terraform to build an Azure Application Gateway with:
12 | - a Monitoring Dashboard hosted on a Log Analytics Workspace,
13 | - and a Key Vault as a safeguard of Web TLS/SSL certificates.
14 | #Azure #ApplicationGateway #Terraform #KeyVault #LogMonitor
15 |
16 | Authentication
17 | ------------
18 | Currently we use Authentication through AZ CLI : https://www.terraform.io/docs/providers/azurerm/guides/azure_cli.html
19 | ```
20 | az login
21 | az account set --subscription="mvp-sub1"
22 | ```
23 |
24 |
25 | Sample usage on Net
26 | -----
27 |
28 | This step ensures that Terraform has all the prerequisites to build your template in Azure.
29 | ```hcl
30 |
31 | terraform init
32 |
33 | ```
34 |
35 | The terraform plan command is used to create an execution plan.
36 | This step compares the requested resources to the state information saved by Terraform and then gives as an output the planned execution. Resources are not created in Azure.
37 | ```hcl
38 | terraform plan
39 | ```
40 |
41 | If all is ok with the proposal you can now apply the configuration.
42 | ```hcl
43 | terraform apply
44 | ```
45 |
--------------------------------------------------------------------------------
/azurerm_application_gateway/provider.tf:
--------------------------------------------------------------------------------
1 | #Set the terraform backend
2 | terraform {
3 | backend "azurerm" {
4 | resource_group_name = "infr-jdld-noprd-rg1"
5 | storage_account_name = "infrasdbx1vpcjdld1" #Name must be unique
6 | container_name = "tfstate"
7 | key = "appgw.tfstate"
8 | }
9 | #required_version = "0.13.3"
10 | }
11 |
12 | #Set the Provider
13 | provider "azurerm" {
14 | #version = "2.28.0"
15 | skip_provider_registration = true #(Optional) Should the AzureRM Provider skip registering the Resource Providers it supports? This can also be sourced from the ARM_SKIP_PROVIDER_REGISTRATION Environment Variable. Defaults to false.
16 | features {}
17 |
18 | #subscription_id = var.subscription_id
19 | # client_id = var.client_id
20 | # client_secret = var.client_secret
21 | # tenant_id = var.tenant_id
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/module/Add-AzureRmLoadBalancerOutboundRules/AzureRmLoadBalancerOutboundRules_template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://schema.management.azure.com/schemas/2018-05-01/deploymentTemplate.json",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "apiVersion": {
6 | "defaultValue": "2018-08-01",
7 | "type": "string",
8 | "metadata": {
9 | "description": "API version 2018-08-01 permits an outbound rule definition structured : https://docs.microsoft.com/en-us/azure/load-balancer/load-balancer-outbound-rules-overview"
10 | }
11 | },
12 | "lbName": {
13 | "type": "string",
14 | "metadata": {
15 | "description": "Load balancer name."
16 | }
17 | },
18 | "tags": {
19 | "type": "string",
20 | "defaultValue": "{\"APP\":\"XXX\",\"BUD\":\"XXX\",\"CTC\":\"XXX@XXX\",\"ENV\":\"XXX\"}",
21 | "metadata": {
22 | "description": "Tags of the Vault"
23 | }
24 | },
25 | "sku": {
26 | "type": "string",
27 | "metadata": {
28 | "description": "Load balancer and public IP sku."
29 | },
30 | "allowedValues": [
31 | "Standard"
32 | ]
33 | },
34 | "publicIPAllocationMethod": {
35 | "type": "string",
36 | "defaultValue": "Static",
37 | "metadata": {
38 | "description": "The public IP allocation method. Possible values are: 'Static' and 'Dynamic'. - Static or Dynamic."
39 | }
40 | },
41 | "allocatedOutboundPorts": {
42 | "type": "string",
43 | "metadata": {
44 | "description": "Number of SNAT ports, Load Balancer allocates SNAT ports in multiples of 8."
45 | }
46 | },
47 | "idleTimeoutInMinutes": {
48 | "type": "string",
49 | "metadata": {
50 | "description": "Outbound flow idle timeout. The parameter accepts a value from 4 to 66."
51 | }
52 | },
53 | "enableTcpReset": {
54 | "type": "string",
55 | "defaultValue": "false",
56 | "metadata": {
57 | "description": "Enable TCP Reset on idle timeout."
58 | }
59 | },
60 | "protocol": {
61 | "type": "string",
62 | "metadata": {
63 | "description": "Transport protocol of the outbound rule."
64 | },
65 | "allowedValues": [
66 | "Tcp",
67 | "Udp",
68 | "All"
69 | ]
70 | },
71 | "lb_public_ip_id": {
72 | "type": "string",
73 | "defaultValue": "",
74 | "metadata": {
75 | "description": "Id of an existing public ip."
76 | }
77 | }
78 | },
79 | "variables": {
80 | "enableTcpReset": "[contains(parameters('enableTcpReset'), 'true')]",
81 | "publicIPAddressName": "[concat(parameters('lbName'), '_pip1')]",
82 | "outboundRuleName": "[concat(parameters('lbName'), '_outrule1')]",
83 | "frontend_ip_configuration_name": "[concat(parameters('lbName'), '_lbcfg1')]",
84 | "backendAddressPoolName": "[concat(parameters('lbName'), '_bckpool1')]",
85 | "lbID": "[resourceId('Microsoft.Network/loadBalancers',parameters('lbName'))]",
86 | "publicIPAddressID": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]",
87 | "condition_publicIPAddressID": "[if(empty(parameters('lb_public_ip_id')), variables('publicIPAddressID'), parameters('lb_public_ip_id'))]",
88 | "outboundRuleID": "[concat(variables('lbID'), '/outboundRules/', variables('outboundRuleName'))]",
89 | "frontendIPConfigurationID": "[concat(variables('lbID'), '/frontendIPConfigurations/', variables('frontend_ip_configuration_name'))]",
90 | "backendAddressPoolId": "[concat(variables('lbID'),'/backendAddressPools/', variables('backendAddressPoolName'))]",
91 | "tagsbase64": "[base64(parameters('tags'))]"
92 | },
93 | "resources": [
94 | {
95 | "apiVersion": "2018-08-01",
96 | "type": "Microsoft.Network/publicIPAddresses",
97 | "name": "[variables('publicIPAddressName')]",
98 | "location": "[resourceGroup().location]",
99 | "condition": "[empty(parameters('lb_public_ip_id'))]",
100 | "tags": "[base64ToJson(variables('tagsbase64'))]",
101 | "sku": {
102 | "name": "[parameters('sku')]"
103 | },
104 | "properties": {
105 | "publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]"
106 | }
107 | },
108 | {
109 | "apiVersion": "[parameters('apiVersion')]",
110 | "name": "[parameters('lbName')]",
111 | "type": "Microsoft.Network/loadBalancers",
112 | "location": "[resourceGroup().location]",
113 | "tags": "[base64ToJson(variables('tagsbase64'))]",
114 | "sku": {
115 | "name": "[parameters('sku')]"
116 | },
117 | "dependsOn": [
118 | "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]"
119 | ],
120 | "properties": {
121 | "frontendIPConfigurations": [
122 | {
123 | "name": "[variables('frontend_ip_configuration_name')]",
124 | "properties": {
125 | "publicIPAddress": {
126 | "id": "[variables('condition_publicIPAddressID')]"
127 | }
128 | }
129 | }
130 | ],
131 | "backendAddressPools": [
132 | {
133 | "name": "[variables('backendAddressPoolName')]"
134 | }
135 | ],
136 | "outboundRules": [
137 | {
138 | "name": "[variables('outboundRuleName')]",
139 | "id": "[variables('outboundRuleID')]",
140 | "properties": {
141 | "frontendIPConfigurations": [
142 | {
143 | "id": "[variables('frontendIPConfigurationID')]"
144 | }
145 | ],
146 | "allocatedOutboundPorts": "[int(parameters('allocatedOutboundPorts'))]",
147 | "idleTimeoutInMinutes": "[int(parameters('idleTimeoutInMinutes'))]",
148 | "enableTcpReset": "[variables('enableTcpReset')]",
149 | "protocol": "[parameters('protocol')]",
150 | "backendAddressPool": {
151 | "id": "[variables('backendAddressPoolId')]"
152 | }
153 | }
154 | }
155 | ]
156 | }
157 | }
158 | ],
159 | "outputs": {
160 | "load_balancer_backend_address_pools_id": {
161 | "type": "string",
162 | "value": "[variables('backendAddressPoolId')]"
163 | }
164 | }
165 | }
--------------------------------------------------------------------------------
/module/Add-AzureRmLoadBalancerOutboundRules/README.md:
--------------------------------------------------------------------------------
1 | Usage
2 | -----
3 | ```hcl
4 | #Set the Provider
5 | provider "azurerm" {
6 | tenant_id = var.tenant_id
7 | subscription_id = var.subscription_id
8 | client_id = var.client_id
9 | client_secret = var.client_secret
10 | version = "~> 2.0"
11 | features {}
12 | }
13 |
14 | #Set authentication variables
15 | variable "tenant_id" {
16 | description = "Azure tenant Id."
17 | }
18 |
19 | variable "subscription_id" {
20 | description = "Azure subscription Id."
21 | }
22 |
23 | variable "client_id" {
24 | description = "Azure service principal application Id."
25 | }
26 |
27 | variable "client_secret" {
28 | description = "Azure service principal application Secret."
29 | }
30 |
31 | #Set resource variables
32 |
33 | variable "lbs_public" {
34 | default = [
35 | {
36 | suffix_name = "testpublic"
37 | sku = "Standard"
38 | allocatedOutboundPorts = "1000" #Number of SNAT ports, Load Balancer allocates SNAT ports in multiples of 8.
39 | idleTimeoutInMinutes = "4" #Outbound flow idle timeout. The parameter accepts a value from 4 to 66.
40 | enableTcpReset = "false" #Enable TCP Reset on idle timeout.
41 | protocol = "All" #Transport protocol of the outbound rule.
42 | },
43 | ]
44 | }
45 |
46 | variable "default_tags" {
47 | default = {
48 | ENV = "sand1"
49 | APP = "JDLD"
50 | BUD = "FR_BXXXXX"
51 | CTC = "j.dumont@veebaze.com"
52 | }
53 | }
54 |
55 | #Call module
56 | module "Add-AzureRmLoadBalancerOutboundRules-Demo" {
57 | source = "github.com/JamesDLD/terraform/module/Add-AzureRmLoadBalancerOutboundRules"
58 | lbs_out = var.lbs_public
59 | lb_out_prefix = "myapp-demo-"
60 | lb_out_suffix = "-publiclb1"
61 | lb_out_resource_group_name = "infr-jdld-noprd-rg1"
62 | lbs_tags = var.default_tags
63 | #Optional : you can use an existing public ip, otherwise it will create one
64 | #lb_public_ip_id = "/subscriptions/mysubid/resourceGroups/tenant-testi-prd-rg/providers/Microsoft.Network/publicIPAddresses/scinfra-testi-prd-pip-0"
65 | }
66 |
67 | ```
--------------------------------------------------------------------------------
/module/Add-AzureRmLoadBalancerOutboundRules/main.tf:
--------------------------------------------------------------------------------
1 | resource "azurerm_template_deployment" "lb_to_addOutboundRule" {
2 | name = "${var.lbs_out[0]["suffix_name"]}-bck-DEP"
3 | resource_group_name = var.lb_out_resource_group_name
4 | template_body = file(
5 | "${path.module}/AzureRmLoadBalancerOutboundRules_template.json",
6 | )
7 | deployment_mode = "Incremental"
8 |
9 | parameters = {
10 | lbName = "${var.lb_out_prefix}${var.lbs_out[0]["suffix_name"]}${var.lb_out_suffix}"
11 | tags = jsonencode(var.lbs_tags)
12 | sku = var.lbs_out[0]["sku"]
13 | allocatedOutboundPorts = var.lbs_out[0]["allocatedOutboundPorts"]
14 | idleTimeoutInMinutes = var.lbs_out[0]["idleTimeoutInMinutes"]
15 | enableTcpReset = var.lbs_out[0]["enableTcpReset"]
16 | protocol = var.lbs_out[0]["protocol"]
17 | lb_public_ip_id = var.lb_public_ip_id
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/module/Add-AzureRmLoadBalancerOutboundRules/output.tf:
--------------------------------------------------------------------------------
1 | output "lb_to_addOutboundRule_deployment_name" {
2 | value = azurerm_template_deployment.lb_to_addOutboundRule.name
3 | }
4 |
5 | output "lb_backend_id" {
6 | value = azurerm_template_deployment.lb_to_addOutboundRule.outputs["load_balancer_backend_address_pools_id"]
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/module/Add-AzureRmLoadBalancerOutboundRules/var.tf:
--------------------------------------------------------------------------------
1 | variable "lbs_out" {
2 | description = "Load balancer properties."
3 | type = list(object({
4 | suffix_name = string
5 | sku = string
6 | allocatedOutboundPorts = number #Number of SNAT ports, Load Balancer allocates SNAT ports in multiples of 8.
7 | idleTimeoutInMinutes = number #Outbound flow idle timeout. The parameter accepts a value from 4 to 66.
8 | enableTcpReset = bool #Enable TCP Reset on idle timeout.
9 | protocol = string #Transport protocol of the outbound rule.
10 | }))
11 | }
12 |
13 | variable "lb_out_prefix" {
14 | type=string
15 | description = "Prefix used to set a common naming convention on the lb objects."
16 | }
17 |
18 | variable "lb_out_suffix" {
19 | type=string
20 | description = "Prefix used to set a common naming convention on the lb objects."
21 | }
22 |
23 | variable "lb_out_resource_group_name" {
24 | type=string
25 | description = "Load balancer resource group name."
26 | }
27 |
28 | variable "lbs_tags" {
29 | type = map(string)
30 | description = "Load balancer tags."
31 | }
32 |
33 | variable "lb_public_ip_id" {
34 | type=string
35 | description = "Id of an existing public ip"
36 | default = ""
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/module/Add-AzureRmLoadBalancerOutboundRules/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 0.12"
3 | }
4 |
--------------------------------------------------------------------------------
/module/Az-Bastion/AzureRmBastion_template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "location": {
6 | "type": "string",
7 | "metadata": {
8 | "description": "Location of the Bastion."
9 | }
10 | },
11 | "resourceGroupName": {
12 | "type": "string",
13 | "metadata": {
14 | "description": "Resource group name of the Bastion."
15 | }
16 | },
17 | "bastionHostName": {
18 | "type": "string",
19 | "metadata": {
20 | "description": "Name of the Bastion."
21 | }
22 | },
23 | "subnetName": {
24 | "type": "string",
25 | "metadata": {
26 | "description": "Subnet name of the Bastion."
27 | },
28 | "defaultValue": "AzureBastionSubnet"
29 | },
30 | "publicIpAddressName": {
31 | "type": "string",
32 | "metadata": {
33 | "description": "Public IP address name."
34 | }
35 | },
36 | "existingVNETName": {
37 | "type": "string",
38 | "metadata": {
39 | "description": "Vnet name of the Bastion."
40 | }
41 | },
42 | "subnetAddressPrefix": {
43 | "type": "string",
44 | "metadata": {
45 | "description": "Subnet prefix that is dedicated to the Bastion."
46 | }
47 | },
48 | "tags": {
49 | "type": "string",
50 | "defaultValue": "{\"APP\":\"XXX\",\"BUD\":\"XXX\",\"CTC\":\"XXX@XXX\",\"ENV\":\"XXX\"}",
51 | "metadata": {
52 | "description": "Tags of the Bastion and it's public IP."
53 | }
54 | }
55 | },
56 | "variables": {
57 | "tagsbase64": "[base64(parameters('tags'))]"
58 | },
59 | "resources": [
60 | {
61 | "apiVersion": "2019-02-01",
62 | "type": "Microsoft.Network/publicIpAddresses",
63 | "name": "[parameters('publicIpAddressName')]",
64 | "location": "[parameters('location')]",
65 | "sku": {
66 | "name": "Standard"
67 | },
68 | "properties": {
69 | "publicIPAllocationMethod": "Static"
70 | },
71 | "tags": "[base64ToJson(variables('tagsbase64'))]"
72 | },
73 | {
74 | "apiVersion": "2018-04-01",
75 | "type": "Microsoft.Network/virtualNetworks/subnets",
76 | "name": "[concat(parameters('existingVNETName'), '/', parameters('subnetName'))]",
77 | "location": "[parameters('location')]",
78 | "properties": {
79 | "addressPrefix": "[parameters('subnetAddressPrefix')]"
80 | }
81 | },
82 | {
83 | "apiVersion": "2018-10-01",
84 | "type": "Microsoft.Network/bastionHosts",
85 | "name": "[parameters('bastionHostName')]",
86 | "location": "[parameters('location')]",
87 | "dependsOn": [
88 | "[resourceId(parameters('resourceGroupName'), 'Microsoft.Network/publicIpAddresses', parameters('publicIpAddressName'))]"
89 | ],
90 | "properties": {
91 | "ipConfigurations": [
92 | {
93 | "name": "[concat(parameters('bastionHostName'),'-CFG')]",
94 | "properties": {
95 | "subnet": {
96 | "id": "[resourceId(parameters('resourceGroupName'), 'Microsoft.Network/virtualNetworks/subnets', parameters('existingVNETName'),parameters('subnetName'))]"
97 | },
98 | "publicIPAddress": {
99 | "id": "[resourceId(parameters('resourceGroupName'), 'Microsoft.Network/publicIpAddresses', parameters('publicIpAddressName'))]"
100 | }
101 | }
102 | }
103 | ]
104 | },
105 | "tags": "[base64ToJson(variables('tagsbase64'))]"
106 | }
107 | ],
108 | "outputs": {
109 | "resourceID": {
110 | "type": "string",
111 | "value": "[resourceId('Microsoft.Network/bastionHosts', parameters('bastionHostName'))]"
112 | }
113 | }
114 | }
--------------------------------------------------------------------------------
/module/Az-Bastion/README.md:
--------------------------------------------------------------------------------
1 |
2 | Prerequisite
3 | -----
4 | You should register to the preview to be able to create an Azure Bastion. Using PowerShell :
5 | ```
6 | Login-AzAccount
7 | Register-AzProviderFeature -FeatureName AllowBastionHost -ProviderNamespace Microsoft.Network
8 | Register-AzResourceProvider -ProviderNamespace Microsoft.Network
9 | ```
10 |
11 | Wait for few minutes then use the following cmdlet to ensure that you are registred :
12 | ```
13 | Get-AzProviderFeature -ListAvailable
14 | ```
15 |
16 | Usage
17 | -----
18 | ```
19 | #Set the Provider
20 | provider "azurerm" {
21 | tenant_id = var.tenant_id
22 | subscription_id = var.subscription_id
23 | client_id = var.client_id
24 | client_secret = var.client_secret
25 | version = "~> 2.0"
26 | features {}
27 | }
28 |
29 | #Set authentication variables
30 | variable "tenant_id" {
31 | description = "Azure tenant Id."
32 | }
33 |
34 | variable "subscription_id" {
35 | description = "Azure subscription Id."
36 | }
37 |
38 | variable "client_id" {
39 | description = "Azure service principal application Id."
40 | }
41 |
42 | variable "client_secret" {
43 | description = "Azure service principal application Secret."
44 | }
45 |
46 | #Set resource variables
47 |
48 | variable "default_tags" {
49 | default = {
50 | ENV = "sand1"
51 | APP = "JDLD"
52 | BUD = "FR_BXXXXX"
53 | CTC = "j.dumont@veebaze.com"
54 | }
55 | }
56 |
57 | #Call module
58 | module "Az-Bastion-Demo" {
59 | source = "github.com/JamesDLD/terraform/module/Az-Bastion"
60 | location = "westeurope"
61 | resourceGroupName = "infr-jdld-noprd-rg2"
62 | bastionHostName = "apps-bas1"
63 | existingVNETName = "infra-jdld-infr-apps-net1"
64 | publicIpAddressName = "apps-bas1-pip1"
65 | subnetAddressPrefix = "192.168.1.224/27"
66 | tags = var.default_tags
67 | }
68 |
69 | #Module's output
70 | output "bastion_id" {
71 | value = module.Az-Bastion-Demo.bastion_id
72 | }
73 | ```
--------------------------------------------------------------------------------
/module/Az-Bastion/main.tf:
--------------------------------------------------------------------------------
1 | resource "azurerm_template_deployment" "bastion" {
2 | name = "${var.bastionHostName}-dep"
3 | resource_group_name = var.resourceGroupName
4 | template_body = file(
5 | "${path.module}/AzureRmBastion_template.json",
6 | )
7 | deployment_mode = "Incremental"
8 |
9 | parameters = {
10 | location = var.location
11 | resourceGroupName = var.resourceGroupName
12 | bastionHostName = var.bastionHostName
13 | subnetName = var.subnetName
14 | publicIpAddressName = var.publicIpAddressName
15 | existingVNETName = var.existingVNETName
16 | subnetAddressPrefix = var.subnetAddressPrefix
17 | tags = jsonencode(var.tags)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/module/Az-Bastion/output.tf:
--------------------------------------------------------------------------------
1 | output "bastion_id" {
2 | value = azurerm_template_deployment.bastion.outputs["resourceID"]
3 | }
4 |
--------------------------------------------------------------------------------
/module/Az-Bastion/var.tf:
--------------------------------------------------------------------------------
1 |
2 | variable "location" {
3 | type = string
4 | description = "Location of the Bastion."
5 | }
6 |
7 | variable "resourceGroupName" {
8 | type = string
9 | description = "Resource group name of the Bastion."
10 | }
11 |
12 | variable "bastionHostName" {
13 | type = string
14 | description = "Name of the Bastion."
15 | }
16 |
17 | variable "subnetName" {
18 | type = string
19 | description = "Subnet name of the Bastion."
20 | default = "AzureBastionSubnet"
21 | }
22 |
23 | variable "publicIpAddressName" {
24 | type = string
25 | description = "Public IP address name."
26 | }
27 |
28 | variable "existingVNETName" {
29 | type = string
30 | description = "Vnet name of the Bastion."
31 | }
32 |
33 | variable "subnetAddressPrefix" {
34 | type = string
35 | description = "Subnet prefix that is dedicated to the Bastion."
36 | }
37 |
38 | variable "tags" {
39 | type = map(string)
40 | description = "Tags of the bastion."
41 | }
42 |
--------------------------------------------------------------------------------
/module/Az-Bastion/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 0.12"
3 | }
4 |
--------------------------------------------------------------------------------
/module/Az-KeyVault/README.md:
--------------------------------------------------------------------------------
1 | Usage
2 | -----
3 |
4 | ```hcl
5 | #Set the Provider
6 | provider "azurerm" {
7 | tenant_id = var.tenant_id
8 | subscription_id = var.subscription_id
9 | client_id = var.client_id
10 | client_secret = var.client_secret
11 | version = "~> 2.0"
12 | features {}
13 | }
14 | provider "azuread" {
15 | tenant_id = var.tenant_id
16 | subscription_id = var.subscription_id
17 | client_id = var.client_id
18 | client_secret = var.client_secret
19 | }
20 |
21 | #Set authentication variables
22 | variable "tenant_id" {
23 | description = "Azure tenant Id."
24 | }
25 |
26 | variable "subscription_id" {
27 | description = "Azure subscription Id."
28 | }
29 |
30 | variable "client_id" {
31 | description = "Azure service principal application Id."
32 | }
33 |
34 | variable "client_secret" {
35 | description = "Azure service principal application Secret."
36 | }
37 |
38 | #Set resource variables
39 | variable "app_name" {
40 | default = "jdld"
41 | }
42 |
43 | variable "env_name" {
44 | default = "sand1"
45 | }
46 |
47 | variable "default_tags" {
48 | type = map(string)
49 |
50 | default = {
51 | ENV = "sand1"
52 | APP = "JDLD"
53 | BUD = "FR_BXXXXX"
54 | CTC = "j.dumont@veebaze.com"
55 | }
56 | }
57 |
58 | #Call resource / module
59 | data "azuread_service_principal" "demo" {
60 | application_id = var.client_id
61 | }
62 |
63 | module "Az-KeyVault-demo" {
64 | source = "github.com/JamesDLD/terraform/module/Az-KeyVault"
65 | key_vaults = [
66 | {
67 | suffix_name = "sci"
68 | policy1_tenant_id = var.tenant_id
69 | policy1_object_id = data.azuread_service_principal.demo.object_id
70 | policy1_application_id = data.azuread_service_principal.demo.application_id
71 | },
72 | ]
73 | kv_tenant_id = var.tenant_id
74 | kv_prefix = "${var.app_name}-${var.env_name}-"
75 | kv_suffix = "-kv1"
76 | kv_location = "francecentral"
77 | kv_resource_group_name = "infr-jdld-noprd-rg1"
78 | kv_sku = "standard"
79 | kv_tags = var.default_tags
80 | }
81 | ```
--------------------------------------------------------------------------------
/module/Az-KeyVault/main.tf:
--------------------------------------------------------------------------------
1 | resource "azurerm_key_vault" "key_vaults" {
2 | count = "${length(var.key_vaults)}"
3 | name = "${var.kv_prefix}${var.key_vaults[count.index]["suffix_name"]}${var.kv_suffix}"
4 | location = "${var.kv_location}"
5 | resource_group_name = "${var.kv_resource_group_name}"
6 | tenant_id = "${var.kv_tenant_id}"
7 | enabled_for_deployment = true
8 | enabled_for_disk_encryption = true
9 | enabled_for_template_deployment = true
10 | sku_name = "${var.kv_sku}"
11 |
12 | tags = "${var.kv_tags}"
13 |
14 | access_policy {
15 | tenant_id = var.key_vaults[count.index]["policy1_tenant_id"]
16 | object_id = var.key_vaults[count.index]["policy1_object_id"]
17 | application_id = var.key_vaults[count.index]["policy1_application_id"]
18 |
19 | certificate_permissions = [
20 | "create",
21 | "delete",
22 | "deleteissuers",
23 | "get",
24 | "getissuers",
25 | "import",
26 | "list",
27 | "listissuers",
28 | "managecontacts",
29 | "manageissuers",
30 | "setissuers",
31 | "update",
32 | ]
33 |
34 | key_permissions = [
35 | "backup",
36 | "create",
37 | "decrypt",
38 | "delete",
39 | "encrypt",
40 | "get",
41 | "import",
42 | "list",
43 | "purge",
44 | "recover",
45 | "restore",
46 | "sign",
47 | "unwrapKey",
48 | "update",
49 | "verify",
50 | "wrapKey",
51 | ]
52 |
53 | secret_permissions = [
54 | "backup",
55 | "delete",
56 | "get",
57 | "list",
58 | "purge",
59 | "recover",
60 | "restore",
61 | "set",
62 | ]
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/module/Az-KeyVault/output.tf:
--------------------------------------------------------------------------------
1 | output "kv_ids" {
2 | value = azurerm_key_vault.key_vaults.*.id
3 | }
4 |
5 | output "kv_vault_uri" {
6 | value = azurerm_key_vault.key_vaults.*.vault_uri
7 | }
8 |
--------------------------------------------------------------------------------
/module/Az-KeyVault/var.tf:
--------------------------------------------------------------------------------
1 | variable "key_vaults" {
2 | description="List containing your key vaults."
3 | type = list(object({
4 | suffix_name = string
5 | policy1_tenant_id = string
6 | policy1_object_id = string
7 | policy1_application_id = string
8 | }))
9 | }
10 |
11 | variable "kv_prefix" {
12 | description="Key vault prefix name."
13 | type = string
14 | }
15 | variable "kv_suffix" {
16 | description="Key vault suffix name."
17 | type = string
18 | }
19 | variable "kv_location" {
20 | description="Key vault location."
21 | type = string
22 | }
23 | variable "kv_resource_group_name" {
24 | description="Key vault Resource group name."
25 | type = string
26 | }
27 | variable "kv_sku" {
28 | description="Key vault SKU."
29 | type = string
30 | }
31 | variable "kv_tenant_id" {
32 | description="Key vault Azure Tenant Id."
33 | type = string
34 | }
35 |
36 | variable "kv_tags" {
37 | description="Key vault tags."
38 | type = map(string)
39 | }
40 |
--------------------------------------------------------------------------------
/module/Az-KeyVault/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 0.12"
3 | }
4 |
--------------------------------------------------------------------------------
/module/Az-LoadBalancer/README.md:
--------------------------------------------------------------------------------
1 | WARNING
2 | -----
3 | Module has been moved [here on Terraform public registry](https://registry.terraform.io/modules/JamesDLD/Az-LoadBalancer/azurerm).
--------------------------------------------------------------------------------
/module/Az-PolicyAssignment/README.md:
--------------------------------------------------------------------------------
1 | Usage
2 | -----
3 |
4 | ```hcl
5 | #Set the Provider
6 | provider "azurerm" {
7 | tenant_id = var.tenant_id
8 | subscription_id = var.subscription_id
9 | client_id = var.client_id
10 | client_secret = var.client_secret
11 | version = "~> 2.0"
12 | features {}
13 | }
14 |
15 | #Set authentication variables
16 | variable "tenant_id" {
17 | description = "Azure tenant Id."
18 | }
19 |
20 | variable "subscription_id" {
21 | description = "Azure subscription Id."
22 | }
23 |
24 | variable "client_id" {
25 | description = "Azure service principal application Id."
26 | }
27 |
28 | variable "client_secret" {
29 | description = "Azure service principal application Secret."
30 | }
31 |
32 | #Set resource variables
33 | variable "policies" {
34 | default = [
35 | {
36 | suffix_name = "enforce-nsg-on-subnet" #Used to name the policy and to call json template files located into the module's folder
37 | policy_type = "Custom"
38 | mode = "All"
39 | },
40 | {
41 | suffix_name = "enforce-udr-on-subnet" #Used to name the policy and to call json template files located into the module's folder
42 | policy_type = "Custom"
43 | mode = "All"
44 | },
45 | ]
46 | }
47 |
48 | variable "apps_nsgs" {
49 | default = [
50 | {
51 | id = "1"
52 | security_rules = [
53 | {
54 | description = "Demo1"
55 | direction = "Inbound"
56 | name = "ALL_to_NIC_tcp-3389"
57 | access = "Allow"
58 | priority = "2000"
59 | source_address_prefix = "*"
60 | destination_address_prefix = "*"
61 | destination_port_range = "3389"
62 | protocol = "tcp"
63 | source_port_range = "*"
64 | },
65 | ]
66 | },
67 | ]
68 | }
69 |
70 | variable "default_tags" {
71 | default = {
72 | ENV = "sand1"
73 | APP = "JDLD"
74 | BUD = "FR_BXXXXX"
75 | CTC = "j.dumont@veebaze.com"
76 | }
77 | }
78 |
79 | #Call module
80 | module "Az-PolicyDefinition-Demo" {
81 | source = "../../Az-PolicyDefinition"
82 | policies = var.policies
83 | pol_prefix = "ApplicationToto-"
84 | pol_suffix = "-pol1"
85 | }
86 |
87 | resource "azurerm_virtual_network" "demo" {
88 | name = "infra-demo-net1"
89 | location = "francecentral"
90 | resource_group_name = "infr-jdld-noprd-rg1"
91 | address_space = ["10.0.6.0/24", "10.0.7.0/24"]
92 | tags = var.default_tags
93 | }
94 |
95 | resource "azurerm_network_security_group" "demo" {
96 | count = length(var.apps_nsgs)
97 | name = "jdld-demo-nsg${var.apps_nsgs[count.index]["id"]}"
98 | location = "francecentral"
99 | resource_group_name = "infr-jdld-noprd-rg1"
100 |
101 | dynamic "security_rule" {
102 | for_each = var.apps_nsgs[count.index]["security_rules"]
103 | content {
104 | description = lookup(security_rule.value, "description", null)
105 | direction = lookup(security_rule.value, "direction", null)
106 | name = lookup(security_rule.value, "name", null)
107 | access = lookup(security_rule.value, "access", null)
108 | priority = lookup(security_rule.value, "priority", null)
109 | source_address_prefix = lookup(security_rule.value, "source_address_prefix", null)
110 | source_address_prefixes = lookup(security_rule.value, "source_address_prefixes", null)
111 | destination_address_prefix = lookup(security_rule.value, "destination_address_prefix", null)
112 | destination_address_prefixes = lookup(security_rule.value, "destination_address_prefixes", null)
113 | destination_port_range = lookup(security_rule.value, "destination_port_range", null)
114 | destination_port_ranges = lookup(security_rule.value, "destination_port_ranges", null)
115 | protocol = lookup(security_rule.value, "protocol", null)
116 | source_port_range = lookup(security_rule.value, "source_port_range", null)
117 | source_port_ranges = lookup(security_rule.value, "source_port_ranges", null)
118 | }
119 | }
120 | tags = var.default_tags
121 | }
122 |
123 | module "Az-PolicyAssignment-Infra-nsg-on-subnet" {
124 | source = "github.com/JamesDLD/terraform/module/Az-PolicyAssignment"
125 | p_ass_name = "enforce-nsg-on-subnet-for-infra-vnet"
126 | p_ass_scope = element(azurerm_virtual_network.demo.*.id, 0)
127 | p_ass_policy_definition_id = module.Az-PolicyDefinition-Demo.policy_ids[0]
128 | p_ass_key_parameter1 = "nsgId"
129 | p_ass_value_parameter1 = element(azurerm_network_security_group.demo.*.id, 0)
130 | }
131 | ```
--------------------------------------------------------------------------------
/module/Az-PolicyAssignment/main.tf:
--------------------------------------------------------------------------------
1 | data "azurerm_client_config" "current" {
2 | }
3 |
4 | resource "azurerm_policy_assignment" "assignments" {
5 | name = var.p_ass_name
6 | scope = var.p_ass_scope
7 | policy_definition_id = var.p_ass_policy_definition_id
8 | description = "Assigned by App Id : ${data.azurerm_client_config.current.service_principal_application_id}"
9 | display_name = var.p_ass_name
10 |
11 | parameters = <
120 | virtual_network_name = lookup(azurerm_recovery_services_vault.Infr, each.value["rsv_key"], null)["name"]
121 |
122 | Workaround is to perform an explicit depedency-->
123 | */
124 | /*
125 | depends_on = [azurerm_recovery_services_vault.Infr]
126 | recovery_vault_name = "${lookup(var.recovery_services_vault, each.value["rsv_key"], "wrong_rsv_key_in_rsvpol")["prefix"]}-${var.app_name}-${var.env_name}-rsv${lookup(var.recovery_services_vault, each.value["rsv_key"], "wrong_rsv_key_in_rsvpol")["id"]}"
127 | }
128 | */
129 |
--------------------------------------------------------------------------------
/module/Create-AzureRmRecoveryServicesVault/var.tf:
--------------------------------------------------------------------------------
1 | variable "rsv_name" {
2 | description="Recovery services vault name."
3 | type=string
4 | }
5 |
6 | variable "rsv_resource_group_name" {
7 | description="Recovery services vault rg name."
8 | type=string
9 | }
10 |
11 | variable "rsv_tags" {
12 | description="Recovery services vault tags."
13 | type = map(string)
14 | }
15 |
16 | variable "rsv_backup_policies" {
17 | description="Recovery services vault backup policies."
18 | type = list(object({
19 | Name = string
20 | scheduleRunFrequency = string
21 | scheduleRunDays = string
22 | scheduleRunTimes = string
23 | timeZone = string
24 | dailyRetentionDurationCount = number
25 | weeklyRetentionDurationCount = number
26 | monthlyRetentionDurationCount = number
27 | }))
28 | }
--------------------------------------------------------------------------------
/module/Create-AzureRmRecoveryServicesVault/versions.tf:
--------------------------------------------------------------------------------
1 |
2 | terraform {
3 | required_version = ">= 0.12"
4 | }
5 |
--------------------------------------------------------------------------------
/module/Enable-AzureRmVirtualNetworkPeering/output.tf:
--------------------------------------------------------------------------------
1 | output "peers_ops" {
2 | value = azurerm_virtual_network_peering.one_dest_to_source.*.id
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/module/Enable-AzureRmVirtualNetworkPeering/var.tf:
--------------------------------------------------------------------------------
1 | variable "vnet_src_name" {
2 | }
3 |
4 | variable "vnet_rg_src_name" {
5 | }
6 |
7 | variable "vnet_src_id" {
8 | }
9 |
10 | provider "azurerm" {
11 | alias = "src"
12 | }
13 |
14 | variable "Disable_Vnet_Peering" {
15 | }
16 |
17 | #############################################################
18 | ########## ops ##########
19 | #############################################################
20 | variable "list_one" {
21 | description="Virtual network names and resource group names list."
22 | type = list(object({
23 | name = string
24 | resource_group_name = string
25 | }))
26 | }
27 |
28 | provider "azurerm" {
29 | alias = "provider_one"
30 | }
31 |
32 | #############################################################
33 | ########## sec ##########
34 | #############################################################
35 | variable "list_two" {
36 | description="Virtual network names and resource group names list."
37 | type = list(object({
38 | name = string
39 | resource_group_name = string
40 | }))
41 | }
42 |
43 | provider "azurerm" {
44 | alias = "provider_two"
45 | }
46 |
47 | #############################################################
48 | ########## k8s ##########
49 | #############################################################
50 | variable "list_three" {
51 | description="Virtual network names and resource group names list."
52 | type = list(object({
53 | name = string
54 | resource_group_name = string
55 | }))
56 | }
57 |
58 | provider "azurerm" {
59 | alias = "provider_three"
60 | }
61 |
62 | #############################################################
63 | ########## Agile Fabric ##########
64 | #############################################################
65 | variable "list_four" {
66 | description="Virtual network names and resource group names list."
67 | type = list(object({
68 | name = string
69 | resource_group_name = string
70 | }))
71 | }
72 |
73 | provider "azurerm" {
74 | alias = "provider_four"
75 | }
76 |
77 | #############################################################
78 | ########## Pub ##########
79 | #############################################################
80 | variable "list_five" {
81 | description="Virtual network names and resource group names list."
82 | type = list(object({
83 | name = string
84 | resource_group_name = string
85 | }))
86 | }
87 |
88 | provider "azurerm" {
89 | alias = "provider_five"
90 | }
91 |
92 | #############################################################
93 | ########## Extra 1 ##########
94 | #############################################################
95 | variable "list_six" {
96 | description="Virtual network names and resource group names list."
97 | type = list(object({
98 | name = string
99 | resource_group_name = string
100 | }))
101 | }
102 |
103 | provider "azurerm" {
104 | alias = "provider_six"
105 | }
106 |
107 | #############################################################
108 | ########## Extra 2 ##########
109 | #############################################################
110 | variable "list_seven" {
111 | description="Virtual network names and resource group names list."
112 | type = list(object({
113 | name = string
114 | resource_group_name = string
115 | }))
116 | }
117 |
118 | provider "azurerm" {
119 | alias = "provider_seven"
120 | }
121 |
122 | #############################################################
123 | ########## Extra 3 ##########
124 | #############################################################
125 | variable "list_eight" {
126 | description="Virtual network names and resource group names list."
127 | type = list(object({
128 | name = string
129 | resource_group_name = string
130 | }))
131 | }
132 |
133 | provider "azurerm" {
134 | alias = "provider_eight"
135 | }
136 |
137 |
--------------------------------------------------------------------------------
/module/Enable-AzureRmVirtualNetworkPeering/versions.tf:
--------------------------------------------------------------------------------
1 |
2 | terraform {
3 | required_version = ">= 0.12"
4 | }
5 |
--------------------------------------------------------------------------------
/module/Get-AzureRmVirtualNetwork/README.md:
--------------------------------------------------------------------------------
1 | Usage
2 | -----
3 | ```hcl
4 | #Set the Provider
5 | provider "azurerm" {
6 | tenant_id = var.tenant_id
7 | subscription_id = var.subscription_id
8 | client_id = var.client_id
9 | client_secret = var.client_secret
10 | version = "~> 2.0"
11 | features {}
12 | }
13 |
14 | #Set authentication variables
15 | variable "tenant_id" {
16 | description = "Azure tenant Id."
17 | }
18 |
19 | variable "subscription_id" {
20 | description = "Azure subscription Id."
21 | }
22 |
23 | variable "client_id" {
24 | description = "Azure service principal application Id."
25 | }
26 |
27 | variable "client_secret" {
28 | description = "Azure service principal application Secret."
29 | }
30 |
31 | #Set resource variables
32 | #N/A
33 |
34 | #Call module
35 | module "Get-AzureRmVirtualNetwork" {
36 | source = "github.com/JamesDLD/terraform/module/Get-AzureRmVirtualNetwork"
37 | vnets = ["bp1-vnet1"]
38 | vnet_resource_group_name = "infr-jdld-noprd-rg1"
39 | }
40 | ```
--------------------------------------------------------------------------------
/module/Get-AzureRmVirtualNetwork/main.tf:
--------------------------------------------------------------------------------
1 | data "azurerm_virtual_network" "vnets" {
2 | count = length(var.vnets)
3 | name = element(var.vnets, count.index)
4 | resource_group_name = var.vnet_resource_group_name
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/module/Get-AzureRmVirtualNetwork/output.tf:
--------------------------------------------------------------------------------
1 | output "vnet_names" {
2 | value = data.azurerm_virtual_network.vnets.*.name
3 | }
4 |
5 | output "vnet_ids" {
6 | value = data.azurerm_virtual_network.vnets.*.id
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/module/Get-AzureRmVirtualNetwork/var.tf:
--------------------------------------------------------------------------------
1 | variable "vnets" {
2 | type = list(string)
3 | }
4 |
5 | variable "vnet_resource_group_name" {
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/module/Get-AzureRmVirtualNetwork/versions.tf:
--------------------------------------------------------------------------------
1 |
2 | terraform {
3 | required_version = ">= 0.12"
4 | }
5 |
--------------------------------------------------------------------------------
/pipeline/PublishBuildArtifacts.yml:
--------------------------------------------------------------------------------
1 | # Publish Build Artifact
2 | parameters:
3 | ArtifactName: ''
4 |
5 | steps:
6 | - task: PublishBuildArtifacts@1
7 | displayName: '${{ parameters.ArtifactName }} - Publish Artifact: ${{ parameters.ArtifactName }}'
8 | inputs:
9 | PathtoPublish: './ArtifactPublishLocation'
10 | ArtifactName: '${{ parameters.ArtifactName }}'
--------------------------------------------------------------------------------