Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ Notice that:

<br/>

## Organizational Install Configurations

There are four new parameters to configure organizational deployments on the cloud for Sysdig Secure for Cloud :-
1. `include_ouids` - List of AWS Organizational Unit IDs to deploy the Sysdig Secure for Cloud stack resources in.
2. `exclude_ouids` - List of AWS Organizational Unit IDs to exclude deploying the Sysdig Secure for Cloud stack resources in.
3. `include_accounts` - List of AWS Accounts to deploy the Sysdig Secure for Cloud stack resources in.
4. `exclude_accounts` - List of AWS Accounts to exclude deploying the Sysdig Secure for Cloud stack resources in.

Note: module variable `organizational_unit_ids` / `org_units` will be DEPRECATED soon going forward. You can use `include_ouids` instead to achieve the same deployment outcome.

## Best practices

For contributing to existing modules or adding new modules, below are some of the best practices recommended :-
Expand Down
117 changes: 115 additions & 2 deletions modules/config-posture/organizational.tf
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,118 @@ data "aws_organizations_organization" "org" {
}

locals {
org_units_to_deploy = var.is_organizational && length(var.org_units) == 0 ? [for root in data.aws_organizations_organization.org[0].roots : root.id] : var.org_units
# fetch the AWS Root OU
root_org_units = var.is_organizational ? [for root in data.aws_organizations_organization.org[0].roots : root.id] : []
}

# if only excluded ouids provided, fetch children ous in org to filter exclusions
# note: AWS fetches only first level immediate children, since it has no data source to give all OUs recursively
data "aws_organizations_organizational_units" "ou" {
count = var.is_organizational && length(var.include_ouids) == 0 && length(var.exclude_ouids) > 0 ? 1 : 0
parent_id = data.aws_organizations_organization.org[0].roots[0].id
}

#----------------------------------------------------------
# Manage configurations to determine targets to deploy in
#----------------------------------------------------------

locals {
# OU CONFIGURATION (determine user provided org configuration)
org_configuration = (
# case1 - if no include/exclude ous provided, include entire org
var.is_organizational && length(var.include_ouids) == 0 && length(var.exclude_ouids) == 0 ? (
"entire_org"
) : (
# case2 - if only included ouids provided, include those ous only
var.is_organizational && length(var.include_ouids) > 0 && length(var.exclude_ouids) == 0 ? (
"included_ous_only"
) : (
# case3 - if only excluded ouids provided, exclude their accounts from rest of org
var.is_organizational && length(var.include_ouids) == 0 && length(var.exclude_ouids) > 0 ? (
"excluded_ous_only"
) : (
# case4 - if both include and exclude ouids are provided, includes override excludes
var.is_organizational && length(var.include_ouids) > 0 && length(var.exclude_ouids) > 0 ? (
"mixed_ous"
) : ""
)
)
)
)

# handling exclusions when only excluded ouids are provided
# fetch list of all ouids to filter exclusions (AWS data source only returns first level immediate children)
oulist = local.org_configuration == "excluded_ous_only" ? toset([for ou in data.aws_organizations_organizational_units.ou[0].children: ou.id]) : toset([])

# switch cases for various user provided org configuration to be onboarded
deployment_options = {
entire_org = {
org_units_to_deploy = local.root_org_units
}
included_ous_only = {
org_units_to_deploy = var.include_ouids
}
excluded_ous_only = {
# check if user provided excluded ouids are in oulist to determine whether or not we can make exclusions, else we ignore and onboard entire org
# TODO: update this if we find alternative to get all OUs in tree to filter exclusions for nested ouids as well
org_units_to_deploy = length(setintersection(local.oulist, var.exclude_ouids)) > 0 ? setsubtract(local.oulist, var.exclude_ouids) : local.root_org_units
}
mixed_ous = {
# if both include and exclude ouids are provided, includes override excludes
org_units_to_deploy = var.include_ouids
}
default = {
org_units_to_deploy = []
}
}

# final targets to deploy organizational resources in
deployment_targets = lookup(local.deployment_options, local.org_configuration, local.deployment_options.default)
}

locals {
# ACCOUNTS CONFIGURATION (determine user provided accounts configuration)
accounts_configuration = (
# case1 - if no include/exclude accounts provided
var.is_organizational && length(var.include_accounts) == 0 && length(var.exclude_accounts) == 0 ? (
"NONE"
) : (
# case2 - if only included accounts provided, include those accts as well
var.is_organizational && length(var.include_accounts) > 0 && length(var.exclude_accounts) == 0 ? (
"UNION"
) : (
# case3 - if only excluded accounts provided, exclude those accounts
var.is_organizational && length(var.include_accounts) == 0 && length(var.exclude_accounts) > 0 ? (
"DIFFERENCE"
) : (
# case4 - if both include and exclude accounts are provided, includes override excludes
# TODO: update this mixed case if we find an alternative to pass both inclusion & exclusion of accounts
var.is_organizational && length(var.include_accounts) > 0 && length(var.exclude_accounts) > 0 ? (
"UNION"
) : ""
)
)
)
)

# switch cases for various user provided accounts configuration to be onboarded
deployment_account_options = {
NONE = {
accounts_to_deploy = []
}
UNION = {
accounts_to_deploy = var.include_accounts
}
DIFFERENCE = {
accounts_to_deploy = var.exclude_accounts
}
default = {
accounts_to_deploy = []
}
}

# list of accounts to deploy organizational resources in
deployment_accounts = lookup(local.deployment_account_options, local.accounts_configuration, local.deployment_account_options.default)
}

#----------------------------------------------------------
Expand Down Expand Up @@ -93,7 +204,9 @@ resource "aws_cloudformation_stack_set_instance" "stackset_instance" {
region = var.region == "" ? null : var.region
stack_set_name = aws_cloudformation_stack_set.stackset[0].name
deployment_targets {
organizational_unit_ids = local.org_units_to_deploy
organizational_unit_ids = local.deployment_targets.org_units_to_deploy
accounts = local.accounts_configuration == "NONE" ? null : local.deployment_accounts.accounts_to_deploy
account_filter_type = local.accounts_configuration
}
operation_preferences {
max_concurrent_percentage = 100
Expand Down
119 changes: 117 additions & 2 deletions modules/onboarding/organizational.tf
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,118 @@ data "aws_organizations_organization" "org" {
}

locals {
org_units_to_deploy = var.is_organizational && length(var.organizational_unit_ids) == 0 ? [for root in data.aws_organizations_organization.org[0].roots : root.id] : var.organizational_unit_ids
# fetch the AWS Root OU
root_org_units = var.is_organizational ? [for root in data.aws_organizations_organization.org[0].roots : root.id] : []
}

# if only excluded ouids provided, fetch children ous in org to filter exclusions
# note: AWS fetches only first level immediate children, since it has no data source to give all OUs recursively
data "aws_organizations_organizational_units" "ou" {
count = var.is_organizational && length(var.include_ouids) == 0 && length(var.exclude_ouids) > 0 ? 1 : 0
parent_id = data.aws_organizations_organization.org[0].roots[0].id
}

#----------------------------------------------------------
# Manage configurations to determine targets to deploy in
#----------------------------------------------------------

locals {
# OU CONFIGURATION (determine user provided org configuration)
org_configuration = (
# case1 - if no include/exclude ous provided, include entire org
var.is_organizational && length(var.include_ouids) == 0 && length(var.exclude_ouids) == 0 ? (
"entire_org"
) : (
# case2 - if only included ouids provided, include those ous only
var.is_organizational && length(var.include_ouids) > 0 && length(var.exclude_ouids) == 0 ? (
"included_ous_only"
) : (
# case3 - if only excluded ouids provided, exclude their accounts from rest of org
var.is_organizational && length(var.include_ouids) == 0 && length(var.exclude_ouids) > 0 ? (
"excluded_ous_only"
) : (
# case4 - if both include and exclude ouids are provided, includes override excludes
var.is_organizational && length(var.include_ouids) > 0 && length(var.exclude_ouids) > 0 ? (
"mixed_ous"
) : ""
)
)
)
)

# handling exclusions when only excluded ouids are provided
# fetch list of all ouids to filter exclusions (AWS data source only returns first level immediate children)
oulist = local.org_configuration == "excluded_ous_only" ? toset([for ou in data.aws_organizations_organizational_units.ou[0].children: ou.id]) : toset([])

# switch cases for various user provided org configuration to be onboarded
deployment_options = {
entire_org = {
org_units_to_deploy = local.root_org_units
}
included_ous_only = {
org_units_to_deploy = var.include_ouids
}
excluded_ous_only = {
# check if user provided excluded ouids are in oulist to determine whether or not we can make exclusions, else we ignore and onboard entire org
# TODO: update this if we find alternative to get all OUs in tree to filter exclusions for nested ouids as well
org_units_to_deploy = length(setintersection(local.oulist, var.exclude_ouids)) > 0 ? setsubtract(local.oulist, var.exclude_ouids) : local.root_org_units
}
mixed_ous = {
# if both include and exclude ouids are provided, includes override excludes
org_units_to_deploy = var.include_ouids
}
default = {
org_units_to_deploy = []
}
}

# final targets to deploy organizational resources in
deployment_targets = lookup(local.deployment_options, local.org_configuration, local.deployment_options.default)
}

locals {
# ACCOUNTS CONFIGURATION (determine user provided accounts configuration)
accounts_configuration = (
# case1 - if no include/exclude accounts provided
var.is_organizational && length(var.include_accounts) == 0 && length(var.exclude_accounts) == 0 ? (
"NONE"
) : (
# case2 - if only included accounts provided, include those accts as well
var.is_organizational && length(var.include_accounts) > 0 && length(var.exclude_accounts) == 0 ? (
"UNION"
) : (
# case3 - if only excluded accounts provided, exclude those accounts
var.is_organizational && length(var.include_accounts) == 0 && length(var.exclude_accounts) > 0 ? (
"DIFFERENCE"
) : (
# case4 - if both include and exclude accounts are provided, includes override excludes
# TODO: update this mixed case if we find an alternative to pass both inclusion & exclusion of accounts
var.is_organizational && length(var.include_accounts) > 0 && length(var.exclude_accounts) > 0 ? (
"UNION"
) : ""
)
)
)
)

# switch cases for various user provided accounts configuration to be onboarded
deployment_account_options = {
NONE = {
accounts_to_deploy = []
}
UNION = {
accounts_to_deploy = var.include_accounts
}
DIFFERENCE = {
accounts_to_deploy = var.exclude_accounts
}
default = {
accounts_to_deploy = []
}
}

# list of accounts to deploy organizational resources in
deployment_accounts = lookup(local.deployment_account_options, local.accounts_configuration, local.deployment_account_options.default)
}

#----------------------------------------------------------
Expand Down Expand Up @@ -71,7 +182,9 @@ resource "aws_cloudformation_stack_set_instance" "stackset_instance" {
region = var.region == "" ? null : var.region
stack_set_name = aws_cloudformation_stack_set.stackset[0].name
deployment_targets {
organizational_unit_ids = local.org_units_to_deploy
organizational_unit_ids = local.deployment_targets.org_units_to_deploy
accounts = local.accounts_configuration == "NONE" ? null : local.deployment_accounts.accounts_to_deploy
account_filter_type = local.accounts_configuration
}
operation_preferences {
max_concurrent_percentage = 100
Expand All @@ -90,6 +203,8 @@ resource "aws_cloudformation_stack_set_instance" "stackset_instance" {
resource "sysdig_secure_organization" "aws_organization" {
count = var.is_organizational ? 1 : 0
management_account_id = sysdig_secure_cloud_auth_account.cloud_auth_account.id
# TODO: once BE change is added
# root_organizational_unit = local.root_org_units[0]
included_organizational_groups = var.include_ouids
excluded_organizational_groups = var.exclude_ouids
included_cloud_accounts = var.include_accounts
Expand Down
9 changes: 6 additions & 3 deletions test/examples/organization/onboarding_with_cspm.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,24 @@ provider "aws" {

module "onboarding" {
source = "../../../modules/onboarding"
is_organizational = true
# legacy org install
# organizational_unit_ids = ["ou-ks5g-dofso0kc"]

# include/exclude params
include_ouids = ["ou-1", "ou-2"]
exclude_accounts = ["123456789101", "123456789101", "123456789101", "123456789101"]
include_accounts = ["123456789101", "123456789101"]
is_organizational = true
}

module "config-posture" {
source = "../../../modules/config-posture"
sysdig_secure_account_id = module.onboarding.sysdig_secure_account_id
org_units = ["ou-ks5g-dofso0kc"]
is_organizational = true
# legacy org install
# org_units = ["ou-ks5g-dofso0kc"]

# include/exclude params
include_ouids = module.onboarding.include_ouids
exclude_ouids = module.onboarding.exclude_ouids
include_accounts = module.onboarding.include_accounts
Expand All @@ -46,4 +49,4 @@ resource "sysdig_secure_cloud_auth_account_feature" "config_posture" {
enabled = true
components = [module.config-posture.config_posture_component_id]
depends_on = [module.config-posture]
}
}