Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fe1d24d
fix storage cmks
matt-pappas-cf Oct 15, 2025
2d9e331
var fix
matt-pappas-cf Oct 15, 2025
b0920c1
codeowners
matt-pappas-cf Oct 15, 2025
8c6f9f0
chore: refresh dependabot.yml & TF sources
matt-pappas-cf Oct 15, 2025
7cb9867
terraform-docs: automated action
github-actions[bot] Oct 15, 2025
8be1776
readme
matt-pappas-cf Oct 15, 2025
74ada2a
readme
matt-pappas-cf Oct 15, 2025
9656473
Merge branch 'fix/storage-cmk' of https://github.com/Coalfire-CF/terr…
matt-pappas-cf Oct 15, 2025
7ee630a
diag version
matt-pappas-cf Oct 15, 2025
b63271a
terraform-docs: automated action
github-actions[bot] Oct 15, 2025
1eb2c87
review fixes
matt-pappas-cf Oct 15, 2025
a92bb8f
Merge branch 'fix/storage-cmk' of https://github.com/Coalfire-CF/terr…
matt-pappas-cf Oct 15, 2025
c283cb0
terraform-docs: automated action
github-actions[bot] Oct 15, 2025
30dd534
review fixes
matt-pappas-cf Oct 15, 2025
b72a4bf
Merge branch 'fix/storage-cmk' of https://github.com/Coalfire-CF/terr…
matt-pappas-cf Oct 15, 2025
ab5a94c
review fixes
matt-pappas-cf Oct 15, 2025
c186cce
review fixes
matt-pappas-cf Oct 15, 2025
e28a334
terraform-docs: automated action
github-actions[bot] Oct 15, 2025
788b4c2
test var updates
matt-pappas-cf Oct 15, 2025
79beb31
Merge branch 'fix/storage-cmk' of https://github.com/Coalfire-CF/terr…
matt-pappas-cf Oct 15, 2025
7cf595b
terraform-docs: automated action
github-actions[bot] Oct 15, 2025
f115aa6
test var updates
matt-pappas-cf Oct 15, 2025
745070f
Merge branch 'fix/storage-cmk' of https://github.com/Coalfire-CF/terr…
matt-pappas-cf Oct 15, 2025
fa58a6a
cleanup
matt-pappas-cf Oct 15, 2025
9eed3d9
cleanup
matt-pappas-cf Oct 15, 2025
fea7377
terraform-docs: automated action
github-actions[bot] Oct 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# These owners will be the default owners for everything in the repo. Unless a later match takes precedence.

* @douglas-f @tkennedy-cf
* @Coalfire-CF/CS-Azure-Codeowners
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ Learn more at [Coalfire OpenSource](https://coalfire.com/opensource).
- Containers
- Storage share
- Lifecycle policy
- CMK key and RBAC Role Assignment
- CMK key and RBAC Role Assignment (CMK Key can either be inputted into module or created dynamically with the Storage Account)
- Monitor diagnostic setting

## Usage

Please review the variables.tf to review the default CMK key created as it can be changed to fit different compliance of environments. Additionally, do not set variable 'cmk_key_name' if you want a CMK to be dynamically created for the Storage Account.

This module can be called as outlined below.

- Create a `local` folder under `terraform/azure`.
Expand Down Expand Up @@ -52,7 +54,7 @@ module "core_sa" {
public_network_access_enabled = true
enable_customer_managed_key = true
cmk_key_vault_id = module.core_kv.id
cmk_key_vault_key_name = azurerm_key_vault_key.tfstate-cmk.name
cmk_key_name = azurerm_key_vault_key.tfstate_cmk.name #Define if you want to have already created CMK set for the Storage Account
storage_containers = [
"tfstate"
]
Expand Down Expand Up @@ -88,14 +90,14 @@ No requirements.

| Name | Source | Version |
|------|--------|---------|
| <a name="module_diag"></a> [diag](#module\_diag) | git::https://github.com/Coalfire-CF/terraform-azurerm-diagnostics | n/a |
| <a name="module_diag"></a> [diag](#module\_diag) | git::https://github.com/Coalfire-CF/terraform-azurerm-diagnostics | v1.1.0 |
| <a name="module_storage_cmk"></a> [storage\_cmk](#module\_storage\_cmk) | git::https://github.com/Coalfire-CF/terraform-azurerm-key-vault/modules/kv_key | v1.1.1 |

## Resources

| Name | Type |
|------|------|
| [azurerm_advanced_threat_protection.main](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/advanced_threat_protection) | resource |
| [azurerm_key_vault_key.cmk](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_key) | resource |
| [azurerm_private_endpoint.sa](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource |
| [azurerm_role_assignment.sa_crypto_user](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource |
| [azurerm_storage_account.main](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_account) | resource |
Expand All @@ -110,11 +112,17 @@ No requirements.
|------|-------------|------|---------|:--------:|
| <a name="input_account_kind"></a> [account\_kind](#input\_account\_kind) | Account Kind for the Storage Account | `string` | `"Storagev2"` | no |
| <a name="input_account_tier"></a> [account\_tier](#input\_account\_tier) | Defines the Tier to use for this storage account. Valid options are Standard and Premium. | `string` | `"Standard"` | no |
| <a name="input_cmk_key_vault_id"></a> [cmk\_key\_vault\_id](#input\_cmk\_key\_vault\_id) | The ID of the Key Vault for Customer Managed Key encryption. | `string` | `null` | no |
| <a name="input_cmk_key_name"></a> [cmk\_key\_name](#input\_cmk\_key\_name) | Name of an existing Key Vault key to use for customer-managed encryption. If null, a new key will be created when enable\_customer\_managed\_key is true. | `string` | `null` | no |
| <a name="input_cmk_key_size"></a> [cmk\_key\_size](#input\_cmk\_key\_size) | The size of the RSA key for CMK | `number` | `4096` | no |
| <a name="input_cmk_key_type"></a> [cmk\_key\_type](#input\_cmk\_key\_type) | The type of key to create for CMK. Use 'RSA-HSM' for FedRAMP High or 'RSA' for standard | `string` | `"RSA"` | no |
| <a name="input_cmk_key_vault_id"></a> [cmk\_key\_vault\_id](#input\_cmk\_key\_vault\_id) | The ID of the Key Vault where the CMK key is or will be stored | `string` | `null` | no |
| <a name="input_cmk_rotation_expire_after"></a> [cmk\_rotation\_expire\_after](#input\_cmk\_rotation\_expire\_after) | Duration after which the key will expire (ISO 8601 format, e.g., P180D for 180 days) | `string` | `"P180D"` | no |
| <a name="input_cmk_rotation_policy_enabled"></a> [cmk\_rotation\_policy\_enabled](#input\_cmk\_rotation\_policy\_enabled) | Enable automatic rotation policy for the CMK key | `bool` | `true` | no |
| <a name="input_cmk_rotation_time_before_expiry"></a> [cmk\_rotation\_time\_before\_expiry](#input\_cmk\_rotation\_time\_before\_expiry) | Time before expiry when rotation should occur (ISO 8601 format, e.g., P30D for 30 days) | `string` | `"P30D"` | no |
| <a name="input_cross_tenant_replication_enabled"></a> [cross\_tenant\_replication\_enabled](#input\_cross\_tenant\_replication\_enabled) | Should cross Tenant replication be enabled? Source storage account is in one AAD tenant, and the destination account is in a different tenant. | `bool` | `false` | no |
| <a name="input_diag_log_analytics_id"></a> [diag\_log\_analytics\_id](#input\_diag\_log\_analytics\_id) | ID of the Log Analytics workspace diag settings should be stored in. | `string` | n/a | yes |
| <a name="input_enable_advanced_threat_protection"></a> [enable\_advanced\_threat\_protection](#input\_enable\_advanced\_threat\_protection) | Whether advanced threat protection is enabled. | `bool` | `false` | no |
| <a name="input_enable_customer_managed_key"></a> [enable\_customer\_managed\_key](#input\_enable\_customer\_managed\_key) | Whether the storage account should be encrypted with customer managed keys. | `bool` | `false` | no |
| <a name="input_enable_customer_managed_key"></a> [enable\_customer\_managed\_key](#input\_enable\_customer\_managed\_key) | Enable customer-managed key encryption for the storage account | `bool` | `true` | no |
| <a name="input_endpoint_subnet_id"></a> [endpoint\_subnet\_id](#input\_endpoint\_subnet\_id) | The ID of the Subnet from which Private IP Addresses will be allocated for this Private Endpoint. | `string` | `null` | no |
| <a name="input_identity_ids"></a> [identity\_ids](#input\_identity\_ids) | Specifies a list of User Assigned Managed Identity IDs to be assigned to this Storage Account. | `list(string)` | `null` | no |
| <a name="input_ip_rules"></a> [ip\_rules](#input\_ip\_rules) | List of public IP or IP ranges in CIDR Format. Only IPv4 addresses are allowed. Private IP address ranges are not allowed. | `list(string)` | `null` | no |
Expand Down
42 changes: 26 additions & 16 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -59,32 +59,42 @@ resource "azurerm_role_assignment" "sa_crypto_user" {
principal_id = azurerm_storage_account.main.identity.0.principal_id
}

resource "azurerm_key_vault_key" "cmk" {
count = var.enable_customer_managed_key ? 1 : 0
# Create a new CMK key only if not provided
module "storage_cmk" {
count = var.enable_customer_managed_key && var.cmk_key_name == null ? 1 : 0
source = "git::https://github.com/Coalfire-CF/terraform-azurerm-key-vault/modules/kv_key?ref=v1.1.1"

name = "${azurerm_storage_account.main.name}-cmk"
key_type = var.cmk_key_type
key_vault_id = var.cmk_key_vault_id
key_type = "RSA"
key_size = 4096

key_opts = [
"decrypt",
"encrypt",
"sign",
"unwrapKey",
"verify",
"wrapKey",
]
key_size = var.cmk_key_size

# Custom rotation policy
rotation_policy_enabled = var.cmk_rotation_policy_enabled
rotation_expire_after = var.cmk_rotation_expire_after
rotation_time_before_expiry = var.cmk_rotation_time_before_expiry

tags = var.tags

depends_on = [azurerm_role_assignment.sa_crypto_user]
}

# Use provided CMK key name or the newly created one
locals {
cmk_key_name = var.enable_customer_managed_key ? (
var.cmk_key_name != null ? var.cmk_key_name : module.storage_cmk[0].key_name
) : null
}

resource "azurerm_storage_account_customer_managed_key" "main" {
count = var.enable_customer_managed_key ? 1 : 0
storage_account_id = azurerm_storage_account.main.id
key_vault_id = var.cmk_key_vault_id
key_name = azurerm_key_vault_key.cmk.0.name
key_name = local.cmk_key_name

depends_on = [
azurerm_role_assignment.sa_crypto_user,
azurerm_key_vault_key.cmk
module.storage_cmk
]
}

Expand Down Expand Up @@ -117,7 +127,7 @@ resource "azurerm_advanced_threat_protection" "main" {

module "diag" {
#Evaluate if we want to keep this specifically pinned to this version or reference the latest
source = "git::https://github.com/Coalfire-CF/terraform-azurerm-diagnostics"
source = "git::https://github.com/Coalfire-CF/terraform-azurerm-diagnostics?ref=v1.1.0"
diag_log_analytics_id = var.diag_log_analytics_id
resource_id = azurerm_storage_account.main.id
resource_type = "sa"
Expand Down
61 changes: 49 additions & 12 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -121,18 +121,6 @@ variable "storage_shares" {
default = []
}

variable "enable_customer_managed_key" {
type = bool
description = "Whether the storage account should be encrypted with customer managed keys."
default = false
}

variable "cmk_key_vault_id" {
type = string
description = "The ID of the Key Vault for Customer Managed Key encryption."
default = null
}

variable "enable_advanced_threat_protection" {
type = bool
description = "Whether advanced threat protection is enabled."
Expand Down Expand Up @@ -186,3 +174,52 @@ variable "private_dns_zone_id" {
description = "The ID of the private DNS zone to link to the private endpoint if applicable."
default = null
}

### KV CMK KEY VARIABLES ###
variable "cmk_key_name" {
description = "Name of an existing Key Vault key to use for customer-managed encryption. If null, a new key will be created when enable_customer_managed_key is true."
type = string
default = null
}

variable "enable_customer_managed_key" {
description = "Enable customer-managed key encryption for the storage account"
type = bool
default = true
}

variable "cmk_key_vault_id" {
description = "The ID of the Key Vault where the CMK key is or will be stored"
type = string
default = null
}

variable "cmk_key_type" {
description = "The type of key to create for CMK. Use 'RSA-HSM' for FedRAMP High or 'RSA' for standard"
type = string
default = "RSA"
}

variable "cmk_key_size" {
description = "The size of the RSA key for CMK"
type = number
default = 4096
}

variable "cmk_rotation_policy_enabled" {
description = "Enable automatic rotation policy for the CMK key"
type = bool
default = true
}

variable "cmk_rotation_expire_after" {
description = "Duration after which the key will expire (ISO 8601 format, e.g., P180D for 180 days)"
type = string
default = "P180D"
}

variable "cmk_rotation_time_before_expiry" {
description = "Time before expiry when rotation should occur (ISO 8601 format, e.g., P30D for 30 days)"
type = string
default = "P30D"
}