diff --git a/azure_identity_tf/README.md b/azure_identity_tf/README.md new file mode 100644 index 0000000..58b4a04 --- /dev/null +++ b/azure_identity_tf/README.md @@ -0,0 +1 @@ +# Terraform-Stacks-On-Azure \ No newline at end of file diff --git a/azure_identity_tf/main.tf b/azure_identity_tf/main.tf new file mode 100644 index 0000000..ee97cfe --- /dev/null +++ b/azure_identity_tf/main.tf @@ -0,0 +1,65 @@ +# data about the current subscription +data "azurerm_subscription" "current" {} + +# create an app registration +resource "azuread_application" "hcp_terraform" { + display_name = "hcp-terraform-azure" +} + +# create a service principal for the app +resource "azuread_service_principal" "hcp_terraform" { + client_id = azuread_application.hcp_terraform.client_id +} + +# assign the contributor role for the service principal +resource "azurerm_role_assignment" "contributor" { + scope = data.azurerm_subscription.current.id + principal_id = azuread_service_principal.hcp_terraform.object_id + role_definition_name = "Contributor" +} + +# create federated identity credentials for **plan** operations +# for each deployment name +resource "azuread_application_federated_identity_credential" "plan" { + for_each = toset(var.deployment_names) + application_id = azuread_application.hcp_terraform.id + display_name = "stack-deployment-${each.value}-plan" + audiences = ["api://AzureADTokenExchange"] + issuer = "https://app.terraform.io" + description = "Plan operation for deployment '${each.value}'" + subject = join(":", [ + "organization", + var.organization_name, + "project", + var.project_name, + "stack", + var.stack_name, + "deployment", + each.value, + "operation", + "plan" + ]) +} + +# create federated identity credentials for **apply** operations +# for each deployment name +resource "azuread_application_federated_identity_credential" "apply" { + for_each = toset(var.deployment_names) + application_id = azuread_application.hcp_terraform.id + display_name = "stack-deployment-${each.value}-apply" + audiences = ["api://AzureADTokenExchange"] + issuer = "https://app.terraform.io" + description = "Apply operation for deployment '${each.value}'" + subject = join(":", [ + "organization", + var.organization_name, + "project", + var.project_name, + "stack", + var.stack_name, + "deployment", + each.value, + "operation", + "apply" + ]) +} diff --git a/azure_identity_tf/outputs.tf b/azure_identity_tf/outputs.tf new file mode 100644 index 0000000..eee7e8b --- /dev/null +++ b/azure_identity_tf/outputs.tf @@ -0,0 +1,7 @@ +output "configuration" { + value = { + client_id = azuread_service_principal.hcp_terraform.client_id + tenant_id = data.azurerm_subscription.current.tenant_id + subscription_id = data.azurerm_subscription.current.subscription_id + } +} diff --git a/azure_identity_tf/providers.tf b/azure_identity_tf/providers.tf new file mode 100644 index 0000000..83e1081 --- /dev/null +++ b/azure_identity_tf/providers.tf @@ -0,0 +1,20 @@ +terraform { + required_providers { + azuread = { + source = "hashicorp/azuread" + version = "~> 3.0.2" + } + + azurerm = { + source = "hashicorp/azurerm" + version = "~> 4.0" + } + } +} + +provider "azuread" {} + +provider "azurerm" { + subscription_id = "e8760043-8652-49f9-b487-4b27daf3ec7a" + features {} +} diff --git a/azure_identity_tf/variables.tf b/azure_identity_tf/variables.tf new file mode 100644 index 0000000..a13d463 --- /dev/null +++ b/azure_identity_tf/variables.tf @@ -0,0 +1,19 @@ +variable "deployment_names" { + type = list(string) + description = "List of Terraform stack deployment names" +} + +variable "organization_name" { + type = string + description = "HCP Terraform organization name" +} + +variable "project_name" { + type = string + description = "HCP Terraform project name" +} + +variable "stack_name" { + type = string + description = "Terraform stack name" +} diff --git a/components.tfstack.hcl b/components.tfstack.hcl new file mode 100644 index 0000000..74250ae --- /dev/null +++ b/components.tfstack.hcl @@ -0,0 +1,48 @@ +component "demo_infra_stack_01" { + source = "./stacks/infrastructure_compute" + + inputs = { + demo_compute_resource_group = { + environment = var.central_variables.environment + location = var.central_variables.location + tags = var.central_variables.tags + } + + demo_storage_account = { + account_tier = var.demo_storage_account.account_tier + account_replication_type = var.demo_storage_account.account_replication_type + } + } + + providers = { + azurerm = provider.azurerm.this + random = provider.random.this + } +} + +component "demo_networking_stack_01" { + source = "./stacks/networking" + + inputs = { + demo_networking_resource_group = { + environment = var.central_variables.environment + location = var.central_variables.location + tags = var.central_variables.tags + } + + demo_virtual_network = { + cidr_range = var.demo_virtual_network.cidr_range + tags = var.central_variables.tags + + } + + demo_subnet_01 = { + address_prefixes = var.demo_subnet_01.address_prefixes + } + } + + providers = { + azurerm = provider.azurerm.this + } +} + diff --git a/deployments.tfdeploy.hcl b/deployments.tfdeploy.hcl new file mode 100644 index 0000000..a8d2c84 --- /dev/null +++ b/deployments.tfdeploy.hcl @@ -0,0 +1,70 @@ +identity_token "azurerm" { + audience = [ "api://AzureADTokenExchange" ] +} + +deployment "development" { + inputs = { + + central_variables = { + environment = "dev" + location = "canadacentral" + tags = { + environment = "development" + } + } + + demo_storage_account = { + account_tier = "Standard" + account_replication_type = "LRS" + } + + + demo_virtual_network = { + cidr_range = "10.0.0.0/16" + } + + demo_subnet_01 = { + address_prefixes = ["10.0.16.0/20"] + } + + azurerm_provider = { + identity_token = identity_token.azurerm.jwt + client_id = "1a493a19-46a6-44d0-8033-43530f6b6823" + subscription_id = "e8760043-8652-49f9-b487-4b27daf3ec7a" + tenant_id = "1a93b615-8d62-418a-ac28-22501cf1f978" + } + } +} + +deployment "production" { + inputs = { + central_variables = { + environment = "prod" + location = "canadacentral" + tags = { + environment = "production" + } + } + + demo_storage_account = { + account_tier = "Standard" + account_replication_type = "GZRS" + } + + + demo_virtual_network = { + cidr_range = "10.0.0.0/17" + } + + demo_subnet_01 = { + address_prefixes = ["10.0.16.0/21"] + } + + azurerm_provider = { + identity_token = identity_token.azurerm.jwt + client_id = "1a493a19-46a6-44d0-8033-43530f6b6823" + subscription_id = "cfd475e4-2732-4ec6-b819-5580d3656b25" + tenant_id = "1a93b615-8d62-418a-ac28-22501cf1f978" + } + } +} diff --git a/modules/resource_group/local.tf b/modules/resource_group/local.tf new file mode 100644 index 0000000..5ade10e --- /dev/null +++ b/modules/resource_group/local.tf @@ -0,0 +1,18 @@ +locals { + location_map = { + "Canada Central" = "Canada Central" + "canadacentral" = "Canada Central" + "Canada East" = "Canada East" + "canadaeast" = "Canada East" + } + + location_code_map = { + "Canada Central" = "cc" + "canadacentral" = "cc" + "Canada East" = "ce" + "canadaeast" = "ce" + } + + location = local.location_map[var.location] + location_code = local.location_code_map[var.location] +} \ No newline at end of file diff --git a/modules/resource_group/main.tf b/modules/resource_group/main.tf new file mode 100644 index 0000000..8c105b1 --- /dev/null +++ b/modules/resource_group/main.tf @@ -0,0 +1,6 @@ +resource "azurerm_resource_group" "resource_group" { + name = var.name_override == null ? lower(format("rg-%s-%s-%s", local.location_code, var.environment, var.workload)) : var.name_override + location = var.location + tags = var.tags +} + diff --git a/modules/resource_group/outputs.tf b/modules/resource_group/outputs.tf new file mode 100644 index 0000000..35a9169 --- /dev/null +++ b/modules/resource_group/outputs.tf @@ -0,0 +1,16 @@ +output "name" { + value = azurerm_resource_group.resource_group.name +} + +output "id" { + value = azurerm_resource_group.resource_group.id +} + +output "location" { + value = azurerm_resource_group.resource_group.location +} + + +output "environment" { + value = var.environment +} \ No newline at end of file diff --git a/modules/resource_group/variables.tf b/modules/resource_group/variables.tf new file mode 100644 index 0000000..7437284 --- /dev/null +++ b/modules/resource_group/variables.tf @@ -0,0 +1,34 @@ +variable "name_override" { + type = string + description = "Name override for the resource group" + default = null + +} +variable "workload" { + type = string + description = "Resource type to be stored in the resource group" + +} +variable "location" { + type = string + description = "Azure location" + validation { + condition = contains(["canadacentral", "canadaeast"], var.location) + error_message = "Location must be canadacentral or canadaeast" + } +} + +variable "environment" { + type = string + description = "Environment for the resources" + validation { + condition = contains(["dev", "prod"], var.environment) + error_message = "Environment must be dev or prod" + } + +} +variable "tags" { + type = map(string) + description = "Tags for the resources" + +} \ No newline at end of file diff --git a/modules/storage_account/locals.tf b/modules/storage_account/locals.tf new file mode 100644 index 0000000..5ade10e --- /dev/null +++ b/modules/storage_account/locals.tf @@ -0,0 +1,18 @@ +locals { + location_map = { + "Canada Central" = "Canada Central" + "canadacentral" = "Canada Central" + "Canada East" = "Canada East" + "canadaeast" = "Canada East" + } + + location_code_map = { + "Canada Central" = "cc" + "canadacentral" = "cc" + "Canada East" = "ce" + "canadaeast" = "ce" + } + + location = local.location_map[var.location] + location_code = local.location_code_map[var.location] +} \ No newline at end of file diff --git a/modules/storage_account/main.tf b/modules/storage_account/main.tf new file mode 100644 index 0000000..967c7d6 --- /dev/null +++ b/modules/storage_account/main.tf @@ -0,0 +1,9 @@ +resource "azurerm_storage_account" "this" { + name = var.name_override == null ? lower(format("st%s%s%s", local.location_code, var.environment, var.workload)) : var.name_override + resource_group_name = var.resource_group_name + location = var.location + + access_tier = var.access_tier + account_tier = var.account_tier + account_replication_type = var.account_replication_type +} diff --git a/modules/storage_account/variables.tf b/modules/storage_account/variables.tf new file mode 100644 index 0000000..d50ec83 --- /dev/null +++ b/modules/storage_account/variables.tf @@ -0,0 +1,42 @@ +variable "environment" { + type = string + description = "Prefix for the resources" + +} +variable "name_override" { + type = string + description = "Name override for the resource group" + default = null + +} + +variable "workload" { + type = string + description = "Resource type to be stored in the resource group" + +} + +variable "resource_group_name" { + type = string + description = "Name of the resource group" +} + +variable "location" { + type = string + description = "Azure location for the resources" +} + +variable "access_tier" { + type = string + description = "Access tier for the storage account" +} + +variable "account_tier" { + type = string + description = "Account tier for the storage account" +} + +variable "account_replication_type" { + type = string + description = "Replication type for the storage account" +} \ No newline at end of file diff --git a/modules/subnet/locals.tf b/modules/subnet/locals.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules/subnet/main.tf b/modules/subnet/main.tf new file mode 100644 index 0000000..3e4c449 --- /dev/null +++ b/modules/subnet/main.tf @@ -0,0 +1,7 @@ +resource "azurerm_subnet" "subnet" { + + name = var.name_override == null ? lower(format("snet-%s-%s", var.environment, var.workload)) : var.name_override + resource_group_name = var.resource_group_name + virtual_network_name = var.virtual_network_name + address_prefixes = var.address_prefixes +} \ No newline at end of file diff --git a/modules/subnet/variables.tf b/modules/subnet/variables.tf new file mode 100644 index 0000000..485c884 --- /dev/null +++ b/modules/subnet/variables.tf @@ -0,0 +1,38 @@ +variable "resource_group_name" { + type = string + description = "Name of the resource group" + +} + +variable "virtual_network_name" { + type = string + description = "Name of the virtual network" + +} + +variable "address_prefixes" { + type = list(string) + description = "List of address prefixes for the subnet" + +} + +variable "name_override" { + type = string + description = "Name override for the resource group" + default = null + +} +variable "workload" { + type = string + description = "Resource type to be stored in the resource group" + +} +variable "environment" { + type = string + description = "Environment for the resources" + validation { + condition = contains(["dev", "prod"], var.environment) + error_message = "Environment must be dev or prod" + } + +} \ No newline at end of file diff --git a/modules/virtual_network/locals.tf b/modules/virtual_network/locals.tf new file mode 100644 index 0000000..5ade10e --- /dev/null +++ b/modules/virtual_network/locals.tf @@ -0,0 +1,18 @@ +locals { + location_map = { + "Canada Central" = "Canada Central" + "canadacentral" = "Canada Central" + "Canada East" = "Canada East" + "canadaeast" = "Canada East" + } + + location_code_map = { + "Canada Central" = "cc" + "canadacentral" = "cc" + "Canada East" = "ce" + "canadaeast" = "ce" + } + + location = local.location_map[var.location] + location_code = local.location_code_map[var.location] +} \ No newline at end of file diff --git a/modules/virtual_network/main.tf b/modules/virtual_network/main.tf new file mode 100644 index 0000000..8698e3e --- /dev/null +++ b/modules/virtual_network/main.tf @@ -0,0 +1,9 @@ +resource "azurerm_virtual_network" "virtual_network" { + name = var.name_override == null ? lower(format("vnet-%s-%s-%s", local.location_code, var.environment, var.workload)) : var.name_override + resource_group_name = var.resource_group_name + location = var.location + address_space = [var.cidr_range] + + tags = var.tags +} + diff --git a/modules/virtual_network/outputs.tf b/modules/virtual_network/outputs.tf new file mode 100644 index 0000000..fe13c60 --- /dev/null +++ b/modules/virtual_network/outputs.tf @@ -0,0 +1,12 @@ +output "id" { + value = azurerm_virtual_network.virtual_network.id +} + +output "environment" { + value = var.environment + +} + +output "name" { + value = azurerm_virtual_network.virtual_network.name +} \ No newline at end of file diff --git a/modules/virtual_network/variables.tf b/modules/virtual_network/variables.tf new file mode 100644 index 0000000..ecbd212 --- /dev/null +++ b/modules/virtual_network/variables.tf @@ -0,0 +1,46 @@ +variable "cidr_range" { + description = "CIDR range for the vnet" + type = string +} + +variable "resource_group_name" { + type = string + description = "Name of the resource group" + +} + +variable "name_override" { + type = string + description = "Name override for the resource group" + default = null + +} +variable "workload" { + type = string + description = "Resource type to be stored in the resource group" + +} +variable "location" { + type = string + description = "Azure location" + validation { + condition = contains(["canadacentral", "canadaeast"], var.location) + error_message = "Location must be canadacentral or canadaeast" + } +} + +variable "environment" { + type = string + description = "Environment for the resources" + validation { + condition = contains(["dev", "prod"], var.environment) + error_message = "Environment must be dev or prod" + } + +} +variable "tags" { + type = map(string) + description = "Tags for the resources" + +} + diff --git a/providers.tfstack.hcl b/providers.tfstack.hcl new file mode 100644 index 0000000..c037506 --- /dev/null +++ b/providers.tfstack.hcl @@ -0,0 +1,25 @@ +required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~> 4.0" + } + + random = { + source = "hashicorp/random" + version = "~> 3.6.3" + } +} + +provider "azurerm" "this" { + config { + features {} + use_cli = false + use_oidc = true + oidc_token = var.azurerm_provider.identity_token + client_id = var.azurerm_provider.client_id + subscription_id = var.azurerm_provider.subscription_id + tenant_id = var.azurerm_provider.tenant_id + } +} + +provider "random" "this" {} diff --git a/stacks/components.tfstack.hcl b/stacks/components.tfstack.hcl new file mode 100644 index 0000000..74250ae --- /dev/null +++ b/stacks/components.tfstack.hcl @@ -0,0 +1,48 @@ +component "demo_infra_stack_01" { + source = "./stacks/infrastructure_compute" + + inputs = { + demo_compute_resource_group = { + environment = var.central_variables.environment + location = var.central_variables.location + tags = var.central_variables.tags + } + + demo_storage_account = { + account_tier = var.demo_storage_account.account_tier + account_replication_type = var.demo_storage_account.account_replication_type + } + } + + providers = { + azurerm = provider.azurerm.this + random = provider.random.this + } +} + +component "demo_networking_stack_01" { + source = "./stacks/networking" + + inputs = { + demo_networking_resource_group = { + environment = var.central_variables.environment + location = var.central_variables.location + tags = var.central_variables.tags + } + + demo_virtual_network = { + cidr_range = var.demo_virtual_network.cidr_range + tags = var.central_variables.tags + + } + + demo_subnet_01 = { + address_prefixes = var.demo_subnet_01.address_prefixes + } + } + + providers = { + azurerm = provider.azurerm.this + } +} + diff --git a/stacks/deployments.tfdeploy.hcl b/stacks/deployments.tfdeploy.hcl new file mode 100644 index 0000000..a8d2c84 --- /dev/null +++ b/stacks/deployments.tfdeploy.hcl @@ -0,0 +1,70 @@ +identity_token "azurerm" { + audience = [ "api://AzureADTokenExchange" ] +} + +deployment "development" { + inputs = { + + central_variables = { + environment = "dev" + location = "canadacentral" + tags = { + environment = "development" + } + } + + demo_storage_account = { + account_tier = "Standard" + account_replication_type = "LRS" + } + + + demo_virtual_network = { + cidr_range = "10.0.0.0/16" + } + + demo_subnet_01 = { + address_prefixes = ["10.0.16.0/20"] + } + + azurerm_provider = { + identity_token = identity_token.azurerm.jwt + client_id = "1a493a19-46a6-44d0-8033-43530f6b6823" + subscription_id = "e8760043-8652-49f9-b487-4b27daf3ec7a" + tenant_id = "1a93b615-8d62-418a-ac28-22501cf1f978" + } + } +} + +deployment "production" { + inputs = { + central_variables = { + environment = "prod" + location = "canadacentral" + tags = { + environment = "production" + } + } + + demo_storage_account = { + account_tier = "Standard" + account_replication_type = "GZRS" + } + + + demo_virtual_network = { + cidr_range = "10.0.0.0/17" + } + + demo_subnet_01 = { + address_prefixes = ["10.0.16.0/21"] + } + + azurerm_provider = { + identity_token = identity_token.azurerm.jwt + client_id = "1a493a19-46a6-44d0-8033-43530f6b6823" + subscription_id = "cfd475e4-2732-4ec6-b819-5580d3656b25" + tenant_id = "1a93b615-8d62-418a-ac28-22501cf1f978" + } + } +} diff --git a/stacks/infrastructure_compute/main.tf b/stacks/infrastructure_compute/main.tf new file mode 100644 index 0000000..bb01609 --- /dev/null +++ b/stacks/infrastructure_compute/main.tf @@ -0,0 +1,22 @@ +module "demo_compute_resource_group" { + source = "../../modules/resource_group" + + environment = var.demo_compute_resource_group.environment + location = var.demo_compute_resource_group.location + workload = "compute" + tags = var.demo_compute_resource_group.tags + +} + + +module "demo_storage_account" { + source = "../../modules/storage_account" + + environment = module.demo_compute_resource_group.environment + location = module.demo_compute_resource_group.location + workload = "demo" + resource_group_name = module.demo_compute_resource_group.name + access_tier = "Hot" + account_tier = var.demo_storage_account.account_tier + account_replication_type = var.demo_storage_account.account_replication_type +} \ No newline at end of file diff --git a/stacks/infrastructure_compute/outputs.tf b/stacks/infrastructure_compute/outputs.tf new file mode 100644 index 0000000..5d9d423 --- /dev/null +++ b/stacks/infrastructure_compute/outputs.tf @@ -0,0 +1,4 @@ +output "demo_compute_resource_group_name" { + value = module.demo_compute_resource_group.name + +} \ No newline at end of file diff --git a/stacks/infrastructure_compute/variables.tf b/stacks/infrastructure_compute/variables.tf new file mode 100644 index 0000000..00f5e0c --- /dev/null +++ b/stacks/infrastructure_compute/variables.tf @@ -0,0 +1,19 @@ + + +variable "demo_compute_resource_group" { + type = object({ + environment = string + location = string + tags = map(string) + }) + +} + + +variable "demo_storage_account" { + type = object({ + account_tier = string + account_replication_type = string + }) + +} \ No newline at end of file diff --git a/stacks/networking/main.tf b/stacks/networking/main.tf new file mode 100644 index 0000000..0da4da8 --- /dev/null +++ b/stacks/networking/main.tf @@ -0,0 +1,32 @@ +module "demo_networking_resource_group" { + source = "../../modules/resource_group" + + environment = var.demo_networking_resource_group.environment + location = var.demo_networking_resource_group.location + workload = "network" + tags = var.demo_networking_resource_group.tags + +} + +module "demo_virtual_network" { + source = "../../modules/virtual_network" + + location = module.demo_networking_resource_group.location + cidr_range = var.demo_virtual_network.cidr_range + tags = var.demo_virtual_network.tags + resource_group_name = module.demo_networking_resource_group.name + environment = module.demo_networking_resource_group.environment + workload = "demo" +} + + +module "demo_subnet_01" { + source = "../../modules/subnet" + + address_prefixes = var.demo_subnet_01.address_prefixes + virtual_network_name = module.demo_virtual_network.name + resource_group_name = module.demo_networking_resource_group.name + environment = module.demo_virtual_network.environment + workload = "demo" + +} \ No newline at end of file diff --git a/stacks/networking/outputs.tf b/stacks/networking/outputs.tf new file mode 100644 index 0000000..e69de29 diff --git a/stacks/networking/variables.tf b/stacks/networking/variables.tf new file mode 100644 index 0000000..8269963 --- /dev/null +++ b/stacks/networking/variables.tf @@ -0,0 +1,22 @@ +variable "demo_networking_resource_group" { + type = object({ + environment = string + location = string + tags = map(string) + }) + +} + +variable "demo_virtual_network" { + type = object({ + cidr_range = string + tags = map(string) + }) +} + +variable "demo_subnet_01" { + type = object({ + address_prefixes = list(string) + }) + +} \ No newline at end of file diff --git a/variables.tfstack.hcl b/variables.tfstack.hcl new file mode 100644 index 0000000..4fbee09 --- /dev/null +++ b/variables.tfstack.hcl @@ -0,0 +1,44 @@ + +variable "central_variables" { + type = object({ + environment = string + location = string + tags = map(string) + }) + +} + +variable "azurerm_provider" { + type = object({ + identity_token = string + client_id = string + subscription_id = string + tenant_id = string + }) + ephemeral = true +} + + + + +variable "demo_storage_account" { + type = object({ + account_tier = string + account_replication_type = string + }) +} + + +variable "demo_virtual_network" { + type = object({ + cidr_range = string + }) + +} + +variable "demo_subnet_01" { + type = object({ + address_prefixes = list(string) + }) + +} \ No newline at end of file