Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/buildingblocks standard vnet conf edit #87

Merged
merged 3 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ kit/foundation/docs/template/docs/.vuepress/.temp
# collie files
.collie
foundations/

# Buildingblock configuration provider
generated-provider.tf
3 changes: 2 additions & 1 deletion kit/azure/bootstrap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ upn_domain = "#EXT#@devmeshithesheep.onmicrosoft.com"
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_azuread"></a> [azuread](#requirement\_azuread) | ~> 2.41.0 |
| <a name="requirement_azuread"></a> [azuread](#requirement\_azuread) | ~> 2.45.0 |
| <a name="requirement_azurerm"></a> [azurerm](#requirement\_azurerm) | ~> 3.71.0 |

## Modules
Expand Down Expand Up @@ -109,6 +109,7 @@ upn_domain = "#EXT#@devmeshithesheep.onmicrosoft.com"
| <a name="output_client_principal_id"></a> [client\_principal\_id](#output\_client\_principal\_id) | n/a |
| <a name="output_client_secret"></a> [client\_secret](#output\_client\_secret) | n/a |
| <a name="output_documentation_md"></a> [documentation\_md](#output\_documentation\_md) | n/a |
| <a name="output_module_storage_account_resource_id"></a> [module\_storage\_account\_resource\_id](#output\_module\_storage\_account\_resource\_id) | n/a |
| <a name="output_platform_engineers_azuread_group_displayname"></a> [platform\_engineers\_azuread\_group\_displayname](#output\_platform\_engineers\_azuread\_group\_displayname) | n/a |
| <a name="output_platform_engineers_azuread_group_id"></a> [platform\_engineers\_azuread\_group\_id](#output\_platform\_engineers\_azuread\_group\_id) | n/a |
| <a name="output_platform_engineers_members"></a> [platform\_engineers\_members](#output\_platform\_engineers\_members) | n/a |
Expand Down
4 changes: 4 additions & 0 deletions kit/azure/bootstrap/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ output "platform_engineers_azuread_group_displayname" {
output "platform_engineers_members" {
value = var.platform_engineers_members[*].email
}

output "module_storage_account_resource_id" {
value = module.terraform_state[0].storage_account_resource_id
}
9 changes: 7 additions & 2 deletions kit/azure/bootstrap/resources.deploy-spn.tf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
resource "azurerm_role_definition" "cloudfoundation_deploy" {
name = var.service_principal_name
scope = data.azurerm_management_group.root.id
description = "Permissions required to deploy the cloudfoundation (not operate it)"
description = "Permissions required to deploy the cloudfoundation"

permissions {
actions = [
Expand Down Expand Up @@ -30,7 +30,12 @@ resource "azurerm_role_definition" "cloudfoundation_deploy" {
"Microsoft.Resources/tags/*",

# Permission we need to activate/register required Resource Providers
"*/register/action"
"*/register/action",

# Deployment Permissions
# Permissions to create storage account and containers
"Microsoft.Storage/storageAccounts/*",
"Microsoft.Storage/storageAccounts/blobServices/containers/*"
]
}

Expand Down
4 changes: 4 additions & 0 deletions kit/azure/bootstrap/terraform-state/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ output "resource_group_name" {
value = azurerm_resource_group.tfstates.name
}

output "storage_account_resource_id" {
value = azurerm_storage_account.tfstates.id
}

output "storage_account_name" {
value = azurerm_storage_account.tfstates.name
}
Expand Down
2 changes: 1 addition & 1 deletion kit/azure/bootstrap/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ terraform {

azuread = {
source = "hashicorp/azuread"
version = "~> 2.41.0"
version = "~> 2.45.0"
}
}
}
59 changes: 59 additions & 0 deletions kit/azure/buildingblocks/standard-vnet-configuration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
name: Buildingblocks-azure-standard-vnet-config
summary: |
Prepares the infrastructure to create a new building block definition for "Azure Virtual Network".
---

# Buildingblocks azure virtual network configuration

This module, will creates a new **Service Principal** and a **Storage Account's Container** which then will be leveraged for generating Terraform's Backend and Provider values.

## How to use
- Take the "generated-backend.tf" and "generated-provider.tf" inside of "outputs" folder and drop them as encrypted inputs in your buildingblock definition.
<!-- BEGIN_TF_DOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_azapi"></a> [azapi](#requirement\_azapi) | ~>1.10.0 |
| <a name="requirement_azuread"></a> [azuread](#requirement\_azuread) | ~> 2.45.0 |
| <a name="requirement_azurerm"></a> [azurerm](#requirement\_azurerm) | >= 3.79.0 |

## Modules

No modules.

## Resources

| Name | Type |
|------|------|
| [azapi_resource.container](https://registry.terraform.io/providers/Azure/azapi/latest/docs/resources/resource) | resource |
| [azuread_application.building_blocks](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application) | resource |
| [azuread_application_password.building_blocks_application_pw](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application_password) | resource |
| [azuread_service_principal.building_blocks_spn](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/service_principal) | resource |
| [azurerm_role_assignment.building_blocks](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource |
| [local_file.backend](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource |
| [local_file.provider](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource |
| [time_rotating.building_blocks_secret_rotation](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/rotating) | resource |
| [azurerm_role_definition.builtin](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/role_definition) | data source |
| [azurerm_storage_account.tfstates](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/storage_account) | data source |
| [azurerm_subscription.current](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/subscription) | data source |
| [azurerm_subscription.sta_subscription](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/subscription) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_backend_tf_config_path"></a> [backend\_tf\_config\_path](#input\_backend\_tf\_config\_path) | n/a | `string` | n/a | yes |
| <a name="input_deployment_scope"></a> [deployment\_scope](#input\_deployment\_scope) | The scope where this service principal have access on. Usually in the format of '/providers/Microsoft.Management/managementGroups/0000-0000-0000' | `string` | n/a | yes |
| <a name="input_provider_tf_config_path"></a> [provider\_tf\_config\_path](#input\_provider\_tf\_config\_path) | n/a | `string` | n/a | yes |
| <a name="input_storage_account_resource_id"></a> [storage\_account\_resource\_id](#input\_storage\_account\_resource\_id) | This is the ID of the storage account resource and it retrievable via panel. It is in the format of '/subscription/<sub\_id>/resourcegroups/<rg\_name>/... | `string` | n/a | yes |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_backend_tf"></a> [backend\_tf](#output\_backend\_tf) | Generates a config.tf that can be dropped into meshStack's BuildingBlock Definition as an encrypted file input to configure this building block. |
| <a name="output_provider_tf"></a> [provider\_tf](#output\_provider\_tf) | Generates a config.tf that can be dropped into meshStack's BuildingBlockDefinition as an encrypted file input to configure this building block. |
<!-- END_TF_DOCS -->
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ variable "storage_account_resource_id" {
description = "This is the ID of the storage account resource and it retrievable via panel. It is in the format of '/subscription/<sub_id>/resourcegroups/<rg_name>/..."
}

variable "backend_tf_config_path" {
type = string
}

locals {
sta_resource_id = split("/", "${var.storage_account_resource_id}")
sta_subscription_id = local.sta_resource_id[2]
Expand All @@ -14,35 +18,65 @@ data "azurerm_subscription" "sta_subscription" {
subscription_id = local.sta_subscription_id
}

data "azurerm_storage_account" "tfstates" {
name = local.sta_name
resource_group_name = local.sta_rg_name
}

// There is still an issue when creating a container in a storage account with disabled 'Access Key' in azurerm provider (v3.77).
// We will use 'azapi' instead, until it get fixed in the newer version of Azurerm.
# resource "azurerm_storage_container" "tfstates" {
# name = "tfstates-standard-vnet"
# storage_account_name = data.azurerm_storage_account.tfstates.name
# container_access_type = "blob"
# }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you adding a comment why its commented out or removing it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are creating the container using another provider here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, please remove it (or add a comment why it's there but commented out, but if there is another provider, removing it seems better to me).


resource "azapi_resource" "container" {
type = "Microsoft.Storage/storageAccounts/blobServices/containers@2022-09-01"
name = "tfstates-standard-vnet"
parent_id = "${data.azurerm_storage_account.tfstates.id}/blobServices/default"
body = jsonencode({
properties = {
defaultEncryptionScope = "$account-encryption-key"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Which encryption scope does $account-encryption-key map to? Is it the one from the account and if yes, do we need to set it explicitly?

What's the advantage of a dedicate scope over using the scope created for the storage account?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the default one that the other containers have, in general you can change the encryption key on the blob level if e.g. different teams use a same storage account. can be microsoft managed or customer managed keys

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's the default one, can we leave it out of the definition or does it have to be here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it has to be there, azapi is a bit different, needs more manual intervention

denyEncryptionScopeOverride = true
immutableStorageWithVersioning = {
enabled = false
}
metadata = {}
publicAccess = "None"
}
})
}
output "backend_tf" {
sensitive = true
description = "Generates a config.tf that can be dropped into meshStack's BuildingBlockDefinition as an encrypted file input to configure this building block."
description = "Generates a config.tf that can be dropped into meshStack's BuildingBlock Definition as an encrypted file input to configure this building block."
value = <<EOF
terraform {
backend "azurerm" {
tenant_id = "${data.azurerm_subscription.sta_subscription.tenant_id}"
subscription_id = "${local.sta_subscription_id}"
resource_group_name = "${local.sta_rg_name}"
storage_account_name = "${local.sta_name}"
container_name = "tfstates"
container_name = "${azapi_resource.container.name}"
key = "building-block-standard-vnet"
}
}
EOF
}

resource "local_file" "backend" {
filename = "./outputs/generated-backend.tf"
filename = var.backend_tf_config_path
content = <<-EOT
terraform {
backend "azurerm" {
tenant_id = "${data.azurerm_subscription.sta_subscription.tenant_id}"
subscription_id = "${local.sta_subscription_id}"
resource_group_name = "${local.sta_rg_name}"
storage_account_name = "${local.sta_name}"
container_name = "tfstates"
container_name = "${azapi_resource.container.name}"
key = "building-block-standard-vnet"
}
}
EOT
}

Original file line number Diff line number Diff line change
@@ -1,39 +1,19 @@
locals {
scope = var.deployment_scope
service_principal_name_suffix = var.spn_suffix
deployment_scope = var.deployment_scope
variable "provider_tf_config_path" {
type = string
}

variable "deployment_scope" {
type = string
description = "The scope where this service principal have access on. Usually in the format of '/providers/Microsoft.Management/managementGroups/0000-0000-0000'"
}

variable "spn_suffix" {
type = string
description = "suffix for the SPN's name. The format is 'building_blocks.<SUFFIX>'"
}


data "azurerm_subscription" "current" {

}
//---------------------------------------------------------------------------
// Queries Entra ID for information about well-known application IDs.
// Retrieve details about the service principal
//---------------------------------------------------------------------------

data "azuread_application_published_app_ids" "well_known" {}

data "azuread_service_principal" "msgraph" {
client_id = data.azuread_application_published_app_ids.well_known.result.MicrosoftGraph
}

//---------------------------------------------------------------------------
// Create New application in Microsoft Entra ID
//---------------------------------------------------------------------------
resource "azuread_application" "building_blocks" {
display_name = "building_blocks.${local.service_principal_name_suffix}"
display_name = "building_blocks.standard-vnet"

feature_tags {
enterprise = true
Expand Down Expand Up @@ -62,6 +42,7 @@ resource "time_rotating" "building_blocks_secret_rotation" {
}
resource "azuread_application_password" "building_blocks_application_pw" {
application_id = azuread_application.building_blocks.id

rotate_when_changed = {
rotation = time_rotating.building_blocks_secret_rotation.id
}
Expand All @@ -73,6 +54,7 @@ resource "azuread_application_password" "building_blocks_application_pw" {
resource "azuread_service_principal" "building_blocks_spn" {
client_id = azuread_application.building_blocks.client_id


feature_tags {
enterprise = true
custom_single_sign_on = true
Expand All @@ -88,33 +70,35 @@ data "azurerm_role_definition" "builtin" {
}

resource "azurerm_role_assignment" "building_blocks" {
scope = local.scope
scope = var.deployment_scope
role_definition_id = data.azurerm_role_definition.builtin.id
principal_id = azuread_service_principal.building_blocks_spn.id
}

output "provider_block" {
description = "Generates a config.tf that can be dropped into meshStack's BuildingBlock Definition as an encrypted file input to configure this building block."

output "provider_tf" {

description = "Generates a config.tf that can be dropped into meshStack's BuildingBlockDefinition as an encrypted file input to configure this building block."
sensitive = true
value = <<EOF
provider "azurerm" {
features {}
client_id = "${azuread_service_principal.building_blocks_spn.client_id}"
client_id = "${azuread_application.building_blocks.client_id}"
client_secret = "${azuread_application_password.building_blocks_application_pw.value}"
tenant_id = "${data.azurerm_subscription.current.tenant_id}"
subscription_id = "${data.azurerm_subscription.current.subscription_id}"
}
EOF
}

resource "local_file" "provider" {
filename = "./outputs/generated-provider.tf"
filename = var.provider_tf_config_path
content = <<-EOT
provider "azurerm" {
features {}
client_id = "${azuread_service_principal.building_blocks_spn.client_id}"
client_id = "${azuread_application.building_blocks.client_id}"
client_secret = "${azuread_application_password.building_blocks_application_pw.value}"
tenant_id = "${data.azurerm_subscription.current.tenant_id}"
subscription_id = "${data.azurerm_subscription.current.subscription_id}"
}
EOT
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
include "platform" {
path = find_in_parent_folders("platform.hcl")
expose = true
}

dependency "bootstrap" {
config_path = "${path_relative_from_include()}/bootstrap"
}

terraform {
source = "${get_repo_root()}//kit/azure/buildingblocks/standard-vnet-configuration"
}
generate "provider" {
path = "provider.tf"
if_exists = "overwrite"
contents = <<EOF
provider "azurerm" {
features {}
skip_provider_registration = true
tenant_id = "${include.platform.locals.platform.azure.aadTenantId}"
subscription_id = "${include.platform.locals.platform.azure.subscriptionId}"
client_id = "${dependency.bootstrap.outputs.client_id}"
client_secret = "${dependency.bootstrap.outputs.client_secret}"
}
provider "azapi" {
tenant_id = "${include.platform.locals.platform.azure.aadTenantId}"
subscription_id = "${include.platform.locals.platform.azure.subscriptionId}"
client_id = "${dependency.bootstrap.outputs.client_id}"
client_secret = "${dependency.bootstrap.outputs.client_secret}"
}
EOF
}

inputs = {
# todo: set input variables
storage_account_resource_id = dependency.bootstrap.outputs.module_storage_account_resource_id

#"The scope where this service principal have access on. Usually in the format of '/providers/Microsoft.Management/managementGroups/0000-0000-0000'"
deployment_scope = "/providers/Microsoft.Management/managementGroups/XXXXXXXX"
backend_tf_config_path = "${get_repo_root()}//kit/azure/buildingblocks/standard-vnet-configuration/outputs/generated-backend.tf"
provider_tf_config_path = "${get_repo_root()}//kit/azure/buildingblocks/standard-vnet-configuration/outputs/generated-provider.tf"
}
17 changes: 17 additions & 0 deletions kit/azure/buildingblocks/standard-vnet-configuration/version.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
terraform {
required_version = ">= 1.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 3.79.0"
}
azuread = {
source = "hashicorp/azuread"
version = "~> 2.45.0"
}
azapi = {
source = "Azure/azapi"
version = "~>1.10.0"
}
}
}