Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
36 changes: 26 additions & 10 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 @@ -108,26 +110,40 @@ No requirements.

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_account_kind"></a> [account\_kind](#input\_account\_kind) | Account Kind for the Storage Account | `string` | `"Storagev2"` | no |
| <a name="input_access_tier"></a> [access\_tier](#input\_access\_tier) | Defines the access tier for BlobStorage, FileStorage and StorageV2 accounts. Valid options are Hot, Cool, Cold and Premium. | `string` | `"Hot"` | no |
| <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_allow_nested_items_to_be_public"></a> [allow\_nested\_items\_to\_be\_public](#input\_allow\_nested\_items\_to\_be\_public) | Allow nested items within the storage account to be public. | `bool` | `false` | no |
| <a name="input_blob_properties"></a> [blob\_properties](#input\_blob\_properties) | Blob service properties for advanced features including versioning, soft delete, and CORS configuration.<br/><br/>- change\_feed\_enabled: Enable change feed for the blob service<br/>- change\_feed\_retention\_in\_days: Retention period in days for change feed (1-146000)<br/>- default\_service\_version: Default API version for blob service requests<br/>- last\_access\_time\_enabled: Enable last access time tracking for lifecycle management<br/>- versioning\_enabled: Enable blob versioning<br/>- container\_delete\_retention\_policy: Soft delete retention for deleted containers<br/>- cors\_rule: CORS rules for blob service<br/>- delete\_retention\_policy: Soft delete retention for deleted blobs (1-365 days)<br/>- restore\_policy: Point-in-time restore configuration (requires versioning and delete retention) | <pre>object({<br/> change_feed_enabled = optional(bool, false)<br/> change_feed_retention_in_days = optional(number, null)<br/> default_service_version = optional(string, null)<br/> last_access_time_enabled = optional(bool, false)<br/> versioning_enabled = optional(bool, false)<br/> container_delete_retention_policy = optional(object({<br/> days = number<br/> }), null)<br/> cors_rule = optional(list(object({<br/> allowed_headers = list(string)<br/> allowed_methods = list(string)<br/> allowed_origins = list(string)<br/> exposed_headers = list(string)<br/> max_age_in_seconds = number<br/> })), null)<br/> delete_retention_policy = optional(object({<br/> days = number<br/> }), null)<br/> restore_policy = optional(object({<br/> days = number<br/> }), null)<br/> })</pre> | `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_default_action"></a> [default\_action](#input\_default\_action) | The default action for network rules. Valid options are 'Allow' or 'Deny'. | `string` | `"Deny"` | 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_enable_system_assigned_identity"></a> [enable\_system\_assigned\_identity](#input\_enable\_system\_assigned\_identity) | Enable system-assigned managed identity | `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_https_traffic_only_enabled"></a> [https\_traffic\_only\_enabled](#input\_https\_traffic\_only\_enabled) | Is HTTPS traffic only enabled? | `bool` | `true` | no |
| <a name="input_identity_ids"></a> [identity\_ids](#input\_identity\_ids) | List of user-assigned managed identity IDs | `list(string)` | `null` | no |
| <a name="input_infrastructure_encryption_enabled"></a> [infrastructure\_encryption\_enabled](#input\_infrastructure\_encryption\_enabled) | Is infrastructure encryption enabled? This provides a second layer of encryption at rest for data in the storage account. | `bool` | `true` | 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 |
| <a name="input_is_hns_enabled"></a> [is\_hns\_enabled](#input\_is\_hns\_enabled) | Is Hierarchical Namespace enabled? This can be used with Azure Data Lake Storage Gen 2. | `bool` | `false` | no |
| <a name="input_lifecycle_policies"></a> [lifecycle\_policies](#input\_lifecycle\_policies) | List of lifecycle policies to apply to the storage account. Refer to the documentation for more information. | <pre>list(object({<br/> prefix_match = set(string)<br/> base_blob = optional(object({<br/> tier_to_cool_after_days_since_modification_greater_than = optional(number)<br/> tier_to_cool_after_days_since_last_access_time_greater_than = optional(number)<br/> tier_to_archive_after_days_since_modification_greater_than = optional(number)<br/> tier_to_archive_after_days_since_last_access_time_greater_than = optional(number)<br/> tier_to_archive_after_days_since_last_tier_change_greater_than = optional(number)<br/> delete_after_days_since_modification_greater_than = optional(number)<br/> delete_after_days_since_last_access_time_greater_than = optional(number)<br/> }))<br/> version = optional(object({<br/> tier_to_cool_after_days_since_modification_greater_than = optional(number)<br/> change_tier_to_archive_after_days_since_creation = optional(number)<br/> tier_to_archive_after_days_since_last_tier_change_greater_than = optional(number)<br/> change_tier_to_cool_after_days_since_creation = optional(number)<br/> delete_after_days_since_creation = optional(number)<br/> }))<br/> snapshot = optional(object({<br/> change_tier_to_archive_after_days_since_creation = optional(number)<br/> tier_to_archive_after_days_since_last_tier_change_greater_than = optional(number)<br/> change_tier_to_cool_after_days_since_creation = optional(number)<br/> delete_after_days_since_creation_greater_than = optional(number)<br/> }))<br/> }))</pre> | `null` | no |
| <a name="input_location"></a> [location](#input\_location) | The Azure location/region to create resources in. | `string` | n/a | yes |
| <a name="input_min_tls_version"></a> [min\_tls\_version](#input\_min\_tls\_version) | The minimum TLS version to be permitted on requests to storage. Possible values include: 'TLS1\_0', 'TLS1\_1', 'TLS1\_2'. | `string` | `"TLS1_2"` | no |
| <a name="input_name"></a> [name](#input\_name) | The storage account name | `string` | n/a | yes |
| <a name="input_network_rules_bypass"></a> [network\_rules\_bypass](#input\_network\_rules\_bypass) | Specifies whether traffic is bypassed for Logging/Metrics/AzureServices. Valid options are any combination of Logging, Metrics, AzureServices, or None. | `list(string)` | `null` | no |
| <a name="input_network_rules_bypass"></a> [network\_rules\_bypass](#input\_network\_rules\_bypass) | Specifies whether traffic is bypassed for Logging/Metrics/AzureServices. Valid options are any combination of Logging, Metrics, AzureServices, or None. | `list(string)` | <pre>[<br/> "AzureServices",<br/> "Logging",<br/> "Metrics"<br/>]</pre> | no |
| <a name="input_nfsv3_enabled"></a> [nfsv3\_enabled](#input\_nfsv3\_enabled) | Is NFSv3 protocol enabled. | `bool` | `false` | no |
| <a name="input_private_dns_zone_id"></a> [private\_dns\_zone\_id](#input\_private\_dns\_zone\_id) | The ID of the private DNS zone to link to the private endpoint if applicable. | `string` | `null` | no |
| <a name="input_private_endpoint_subresource_names"></a> [private\_endpoint\_subresource\_names](#input\_private\_endpoint\_subresource\_names) | Subresource name which the private endpoint is able to connect to. | `list(string)` | `[]` | no |
| <a name="input_private_link_access"></a> [private\_link\_access](#input\_private\_link\_access) | List of the resource ids of the endpoint resource to be granted access. | `list(string)` | `[]` | no |
| <a name="input_public_network_access_enabled"></a> [public\_network\_access\_enabled](#input\_public\_network\_access\_enabled) | Whether the public network access is enabled. | `bool` | `false` | no |
| <a name="input_public_network_access_enabled"></a> [public\_network\_access\_enabled](#input\_public\_network\_access\_enabled) | Whether the public network access is enabled. | `bool` | `true` | no |
| <a name="input_replication_type"></a> [replication\_type](#input\_replication\_type) | Defines the type of replication to use for this storage account. Valid options are LRS, GRS, RAGRS, ZRS, GZRS and RAGZRS. Unless you have a specific reason for data without alternate site requirements you should minimum use ZRS | `string` | `"GRS"` | no |
| <a name="input_resource_group_name"></a> [resource\_group\_name](#input\_resource\_group\_name) | The name of the resource group in which to create the resource in. | `string` | n/a | yes |
| <a name="input_static_website"></a> [static\_website](#input\_static\_website) | Enable and configure static website on the storage account. | `map(string)` | `null` | no |
Expand Down
8 changes: 4 additions & 4 deletions fileshare.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
resource "azurerm_storage_share" "main" {
for_each = { for share in var.storage_shares : share.name => share }
name = each.value.name
storage_account_id = azurerm_storage_account.main.id
quota = each.value.quota
for_each = { for share in var.storage_shares : share.name => share }
name = each.value.name
storage_account_id = azurerm_storage_account.main.id
quota = each.value.quota
}
Loading