Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,17 @@ All code contributions made by Lacework customers to this repo are considered
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.9 |
| <a name="requirement_azapi"></a> [azapi](#requirement\_azapi) | ~> 1.15.0 |
| <a name="requirement_azuread"></a> [azuread](#requirement\_azuread) | ~> 2.53.1 |
| <a name="requirement_azurerm"></a> [azurerm](#requirement\_azurerm) | ~> 3.116.0 |
| <a name="requirement_azuread"></a> [azuread](#requirement\_azuread) | ~> 3.4 |
| <a name="requirement_azurerm"></a> [azurerm](#requirement\_azurerm) | ~> 4.37 |
| <a name="requirement_lacework"></a> [lacework](#requirement\_lacework) | ~> 2.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_azapi"></a> [azapi](#provider\_azapi) | ~> 1.15.0 |
| <a name="provider_azuread"></a> [azuread](#provider\_azuread) | ~> 2.53.1 |
| <a name="provider_azurerm"></a> [azurerm](#provider\_azurerm) | ~> 3.116.0 |
| <a name="provider_azuread"></a> [azuread](#provider\_azuread) | ~> 3.4 |
| <a name="provider_azurerm"></a> [azurerm](#provider\_azurerm) | ~> 4.37 |
| <a name="provider_lacework"></a> [lacework](#provider\_lacework) | ~> 2.0 |
| <a name="provider_random"></a> [random](#provider\_random) | n/a |
| <a name="provider_terraform"></a> [terraform](#provider\_terraform) | n/a |
Expand Down Expand Up @@ -112,7 +112,7 @@ No modules.
| <a name="input_execute_now"></a> [execute\_now](#input\_execute\_now) | execute newly created job(s) immediately after deployment | `bool` | `true` | no |
| <a name="input_filter_query_text"></a> [filter\_query\_text](#input\_filter\_query\_text) | The LQL query to constrain the scanning to specific resources. If left blank, Lacework will scan all resources available to the account or organization. For more information, see [Limit Scanned Workloads](https://docs.lacework.net/onboarding/lacework-console-agentless-workload-scanning#aws---limit-scanned-workloads). | `string` | `""` | no |
| <a name="input_global"></a> [global](#input\_global) | Whether we create global resources for this deployment. Defaults to `false` | `bool` | `false` | no |
| <a name="input_global_module_reference"></a> [global\_module\_reference](#input\_global\_module\_reference) | A reference to the global lacework\_azure\_agentless\_scanning module for this account. | <pre>object({<br> scanning_resource_group_name = string<br> scanning_resource_group_id = string<br> key_vault_id = string<br> key_vault_uri = string<br> key_vault_secret_name = string<br> lacework_account = string<br> lacework_domain = string<br> lacework_integration_name = string<br> storage_account_name = string<br> storage_account_id = string<br> blob_container_name = string<br> prefix = string<br> suffix = string<br> monitored_subscription_role_definition_id = string<br> scanning_subscription_role_definition_id = string<br> sidekick_principal_id = string<br> sidekick_client_id = string<br> included_subscriptions = set(string)<br> excluded_subscriptions = set(string)<br> })</pre> | <pre>{<br> "blob_container_name": "",<br> "excluded_subscriptions": [],<br> "included_subscriptions": [],<br> "key_vault_id": "",<br> "key_vault_secret_name": "",<br> "key_vault_uri": "",<br> "lacework_account": "",<br> "lacework_domain": "",<br> "lacework_integration_name": "",<br> "monitored_subscription_role_definition_id": "",<br> "prefix": "",<br> "scanning_resource_group_id": "",<br> "scanning_resource_group_name": "",<br> "scanning_subscription_role_definition_id": "",<br> "sidekick_client_id": "",<br> "sidekick_principal_id": "",<br> "storage_account_id": "",<br> "storage_account_name": "",<br> "suffix": ""<br>}</pre> | no |
| <a name="input_global_module_reference"></a> [global\_module\_reference](#input\_global\_module\_reference) | A reference to the global lacework\_azure\_agentless\_scanning module for this account. | <pre>object({<br> scanning_resource_group_name = string<br> scanning_resource_group_id = string<br> scanning_subscription_id = string<br> key_vault_id = string<br> key_vault_uri = string<br> key_vault_secret_name = string<br> lacework_account = string<br> lacework_domain = string<br> lacework_integration_name = string<br> storage_account_name = string<br> storage_account_id = string<br> blob_container_name = string<br> prefix = string<br> suffix = string<br> monitored_subscription_role_definition_id = string<br> scanning_subscription_role_definition_id = string<br> sidekick_principal_id = string<br> sidekick_client_id = string<br> included_subscriptions = set(string)<br> excluded_subscriptions = set(string)<br> })</pre> | <pre>{<br> "blob_container_name": "",<br> "excluded_subscriptions": [],<br> "included_subscriptions": [],<br> "key_vault_id": "",<br> "key_vault_secret_name": "",<br> "key_vault_uri": "",<br> "lacework_account": "",<br> "lacework_domain": "",<br> "lacework_integration_name": "",<br> "monitored_subscription_role_definition_id": "",<br> "prefix": "",<br> "scanning_resource_group_id": "",<br> "scanning_resource_group_name": "",<br> "scanning_subscription_id": "",<br> "scanning_subscription_role_definition_id": "",<br> "sidekick_client_id": "",<br> "sidekick_principal_id": "",<br> "storage_account_id": "",<br> "storage_account_name": "",<br> "suffix": ""<br>}</pre> | no |
| <a name="input_image_url"></a> [image\_url](#input\_image\_url) | The container image url for Lacework Agentless Workload Scanning. | `string` | `"sidekickpublic.azurecr.io/sidekick:latest"` | no |
| <a name="input_included_subscriptions"></a> [included\_subscriptions](#input\_included\_subscriptions) | List of subscriptions to be monitored. Must be specified with `integration_level = 'SUBSCRIPTION'`. Set only for global resource. | `set(string)` | `[]` | no |
| <a name="input_integration_level"></a> [integration\_level](#input\_integration\_level) | If we are integrating into a subscription or tenant. Valid values are 'SUBSCRIPTION' or 'TENANT' | `string` | n/a | yes |
Expand Down Expand Up @@ -157,6 +157,7 @@ No modules.
| <a name="output_prefix"></a> [prefix](#output\_prefix) | Prefix used to add uniqueness to resource names. |
| <a name="output_scanning_resource_group_id"></a> [scanning\_resource\_group\_id](#output\_scanning\_resource\_group\_id) | Id of the resource group hosting the scanner |
| <a name="output_scanning_resource_group_name"></a> [scanning\_resource\_group\_name](#output\_scanning\_resource\_group\_name) | Name of the resource group hosting the scanner |
| <a name="output_scanning_subscription_id"></a> [scanning\_subscription\_id](#output\_scanning\_subscription\_id) | The subscription ID where scanning resources are deployed |
| <a name="output_scanning_subscription_role_definition_id"></a> [scanning\_subscription\_role\_definition\_id](#output\_scanning\_subscription\_role\_definition\_id) | The id of the scanning subscription role definition |
| <a name="output_sidekick_client_id"></a> [sidekick\_client\_id](#output\_sidekick\_client\_id) | Client id of the managed identity running scanner |
| <a name="output_sidekick_principal_id"></a> [sidekick\_principal\_id](#output\_sidekick\_principal\_id) | The principal id of the user identity used by agentless scanner |
Expand Down
57 changes: 31 additions & 26 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ provider "azurerm" {
prevent_deletion_if_contains_resources = false
}
}
/* use the current resource manager subscription if it's not provided, otherwise
extract the subscription id if it's in the fully qualified form ("/subscriptions/xxx"),
otherwise just use the subscription id as it is.
/* Use scanning_subscription_id from either direct input or global_module_reference
Extract the subscription id if it's in the fully qualified form ("/subscriptions/xxx")
*/
subscription_id = var.scanning_subscription_id == "" ? null : try(
subscription_id = var.scanning_subscription_id != "" ? try(
regex("^/subscriptions/([A-Za-z0-9-_]+)$", var.scanning_subscription_id)[0],
var.scanning_subscription_id
) : (
// Regional modules use the scanning_subscription_id from the global module reference
length(var.global_module_reference.scanning_subscription_id) > 0 ? try(
regex("^/subscriptions/([A-Za-z0-9-_]+)$", var.global_module_reference.scanning_subscription_id)[0],
var.global_module_reference.scanning_subscription_id
) : null
)
}

Expand Down Expand Up @@ -66,12 +71,12 @@ locals {
storage_account_id = var.global ? azurerm_storage_account.scanning[0].id : var.global_module_reference.storage_account_id

included_subscriptions_local = (var.global
? var.included_subscriptions
: var.global_module_reference.included_subscriptions
? var.included_subscriptions
: var.global_module_reference.included_subscriptions
)
excluded_subscriptions_local = (var.global
? var.excluded_subscriptions
: var.global_module_reference.excluded_subscriptions
? var.excluded_subscriptions
: var.global_module_reference.excluded_subscriptions
)

/* Convert the excluded and included subscriptions to the format required by the Lacework AWLS integration
Expand All @@ -81,7 +86,7 @@ locals {
*/
included_subscriptions_list = [for sub in local.included_subscriptions_local : replace(sub, "//subscriptions//", "")]
excluded_subscriptions_list = [for sub in local.excluded_subscriptions_local : replace(sub, "//subscriptions//", "-")]
subscriptions_list = var.integration_level == "SUBSCRIPTION" ? local.included_subscriptions_list : local.excluded_subscriptions_list
subscriptions_list = var.integration_level == "SUBSCRIPTION" ? local.included_subscriptions_list : local.excluded_subscriptions_list

/* Define the scope for the monitored role
- For SUBSCRIPTION integration level, we set the scope to the set of included subscriptions specified by the user
Expand All @@ -90,8 +95,8 @@ locals {
root_management_group_scope = ["/providers/Microsoft.Management/managementGroups/${local.tenant_id}"]
monitored_role_scopes = tolist(
var.integration_level == "SUBSCRIPTION"
? local.included_subscriptions_local
: local.root_management_group_scope
? local.included_subscriptions_local
: local.root_management_group_scope
)

environment_variables = {
Expand All @@ -117,8 +122,8 @@ locals {
AZURE_CONTAINER_REGION = local.container_region
USE_PUBLIC_IPS = local.use_public_ips
}
environment_variables_as_list = concat([for key, val in local.environment_variables : { name = key, value = val }],
[for obj in var.additional_environment_variables : { name = obj["name"], value = obj["value"] }])
environment_variables_as_list = concat([for key, val in local.environment_variables : { name = key, value = val }],
[for obj in var.additional_environment_variables : { name = obj["name"], value = obj["value"] }])

key_vault_id = var.global ? azurerm_key_vault.lw_orchestrate[0].id : (
length(var.global_module_reference.key_vault_id) > 0 ? var.global_module_reference.key_vault_id : var.key_vault_id
Expand All @@ -138,8 +143,8 @@ locals {

custom_network = length(var.custom_network) > 0 ? var.custom_network : (var.regional ? azurerm_subnet.agentless_subnet[0].id : "")

region = lower(replace(var.region, " ", ""))
integration_level = upper(var.integration_level)
region = lower(replace(var.region, " ", ""))
integration_level = upper(var.integration_level)
lacework_integration_name_local = var.global ? var.lacework_integration_name : var.global_module_reference.lacework_integration_name

version_file = "${abspath(path.module)}/VERSION"
Expand All @@ -155,7 +160,7 @@ locals {
australiasoutheast = "australiaeast"
}
container_region = lookup(local.unsupported_region_replacements, local.region, local.region)
use_public_ips = var.use_nat_gateway ? "false" : "true"
use_public_ips = var.use_nat_gateway ? "false" : "true"
}

resource "random_id" "uniq" {
Expand Down Expand Up @@ -210,7 +215,7 @@ resource "lacework_integration_azure_agentless_scanning" "lacework_cloud_account
scan_stopped_instances = var.scan_stopped_instances
query_text = var.filter_query_text
// The Lacework AWLS integration API expects subscription IDs without the "/subscriptions/" prefix
subscriptions_list = local.subscriptions_list
subscriptions_list = local.subscriptions_list
}

/* **************** General ****************
Expand Down Expand Up @@ -238,7 +243,7 @@ resource "azuread_service_principal" "data_loader" {
resource "azuread_service_principal_password" "data_loader" {
count = var.global ? 1 : 0

service_principal_id = azuread_service_principal.data_loader[0].object_id
service_principal_id = azuread_service_principal.data_loader[0].id
end_date_relative = "87600h" // expires in 10 years
}

Expand Down Expand Up @@ -398,7 +403,7 @@ resource "azurerm_role_assignment" "storage_sidekick" {
resource "azurerm_role_assignment" "storage_data_loader" {
count = var.global ? 1 : 0

principal_id = azuread_service_principal.data_loader[0].object_id
principal_id = azuread_service_principal.data_loader[0].id
role_definition_name = "Storage Blob Data Reader"
scope = local.storage_account_id
}
Expand Down Expand Up @@ -489,12 +494,12 @@ resource "azurerm_subnet_network_security_group_association" "agentless_nsg_asso
count = var.regional && length(var.custom_network) == 0 ? 1 : 0

subnet_id = azurerm_subnet.agentless_subnet[0].id
network_security_group_id = length(var.custom_network_security_group) > 0 ? var.custom_network_security_group: azurerm_network_security_group.agentless_orchestrate[0].id
network_security_group_id = length(var.custom_network_security_group) > 0 ? var.custom_network_security_group : azurerm_network_security_group.agentless_orchestrate[0].id
}

resource "azurerm_public_ip" "agentless_public_ip" {
depends_on = [azurerm_resource_group.scanning_rg]
count = var.regional && var.use_nat_gateway ? 1 : 0
count = var.regional && var.use_nat_gateway ? 1 : 0

name = replace("${local.prefix}-public-ip-${local.region}-${local.suffix}", " ", "-")
location = local.region
Expand All @@ -507,11 +512,11 @@ resource "azurerm_public_ip" "agentless_public_ip" {

resource "azurerm_nat_gateway" "agentless_nat_gateway" {
depends_on = [azurerm_resource_group.scanning_rg]
count = var.regional && var.use_nat_gateway ? 1 : 0
count = var.regional && var.use_nat_gateway ? 1 : 0

name = replace("${local.prefix}-nat-gateway-${local.region}-${local.suffix}", " ", "-")
location = local.region
resource_group_name = local.scanning_resource_group_name
name = replace("${local.prefix}-nat-gateway-${local.region}-${local.suffix}", " ", "-")
location = local.region
resource_group_name = local.scanning_resource_group_name

tags = var.tags
}
Expand All @@ -526,7 +531,7 @@ resource "azurerm_nat_gateway_public_ip_association" "agentless_ip_association"
resource "azurerm_subnet_nat_gateway_association" "agentless_nat_gateway_association" {
count = var.regional && var.use_nat_gateway ? 1 : 0

subnet_id = length(var.custom_network) > 0 ? var.custom_network : azurerm_subnet.agentless_subnet[0].id
subnet_id = length(var.custom_network) > 0 ? var.custom_network : azurerm_subnet.agentless_subnet[0].id
nat_gateway_id = azurerm_nat_gateway.agentless_nat_gateway[0].id
}

Expand Down
5 changes: 5 additions & 0 deletions output.tf
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,8 @@ output "excluded_subscriptions" {
value = local.excluded_subscriptions_local
description = "The excluded subscriptions list in global module reference"
}

output "scanning_subscription_id" {
value = data.azurerm_subscription.current.subscription_id
description = "The subscription ID where scanning resources are deployed"
}
2 changes: 2 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ variable "global_module_reference" {
type = object({
scanning_resource_group_name = string
scanning_resource_group_id = string
scanning_subscription_id = string
key_vault_id = string
key_vault_uri = string
key_vault_secret_name = string
Expand All @@ -345,6 +346,7 @@ variable "global_module_reference" {
default = {
scanning_resource_group_name = ""
scanning_resource_group_id = ""
scanning_subscription_id = ""
key_vault_id = ""
key_vault_uri = ""
key_vault_secret_name = ""
Expand Down
4 changes: 2 additions & 2 deletions versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ terraform {
required_providers {
azuread = {
source = "hashicorp/azuread"
version = "~> 2.53.1"
version = "~> 3.4"
}
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.116.0"
version = "~> 4.37"
}
// include azapi because Azure Container App Jobs isn't yet available as a provider
azapi = {
Expand Down