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
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [v0.5.0]

### Added

- Option to provide replicator technical user permission to close accounts based on tag restrictions. This allows for more granular control over which accounts can be closed by the replicator service.
- Possibility to set `meshstack_access_role_name` from the top-level module.

### Removed

- Option to provide replicator technical user permission to close accounts for specified resource org paths (i.e. landing zones) - this feature has been replaced by the tag-based closure configuration, since using Organization Units other than root did not work as expected.

## [v0.4.0]

### Added
Expand Down Expand Up @@ -37,8 +48,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Initial Release

[unreleased]: https://github.com/meshcloud/terraform-aws-meshplatform/compare/v0.4.0...HEAD
[unreleased]: https://github.com/meshcloud/terraform-aws-meshplatform/compare/v0.5.0...HEAD
[v0.1.0]: https://github.com/meshcloud/terraform-aws-meshplatform/releases/tag/v0.1.0
[v0.2.0]: https://github.com/meshcloud/terraform-aws-meshplatform/releases/tag/v0.2.0
[v0.3.0]: https://github.com/meshcloud/terraform-aws-meshplatform/releases/tag/v0.3.0
[v0.4.0]: https://github.com/meshcloud/terraform-aws-meshplatform/releases/tag/v0.4.0
[v0.5.0]: https://github.com/meshcloud/terraform-aws-meshplatform/releases/tag/v0.5.0
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,33 @@ See the `aws` [provider documentation](https://registry.terraform.io/providers/h

For an overview of the module structure, refer to [generated terraform docs](./TERRAFORM_DOCS.md)

## Account Closure Configuration

You can configure which AWS accounts can be closed by meshStack using the `can_close_accounts_with_tags` variable. This allows you to define tag-based restrictions for account closure.

> **Note:** In meshStack, you can configure additional tags for AWS accounts as part of the platform configuration. For more details, see [Tags in Cloud Tenants](https://docs.meshcloud.io/meshstack.metadata-tags/#tags-in-cloud-tenants).

### Example Configuration

```hcl
# No account closure (default) - no accounts can be closed
can_close_accounts_with_tags = {}

# Allow closing accounts with specific tags
# Accounts with Environment=dev, Environment=staging, or Team=platform can be closed
can_close_accounts_with_tags = {
"Environment" = ["dev", "staging"] # Account with Environment=dev OR Environment=staging
"Team" = ["platform"] # Account with Team=platform
}
# Note: Multiple tag keys use OR logic - account needs to match ANY criteria
# Examples: ✓ Environment=dev, ✓ Team=platform, ✗ Environment=prod, ✗ no tags

# Allow closing all accounts without restrictions
can_close_accounts_with_tags = {
"__meshstack_allow_close_all__" = ["true"]
}
```

## How to Use This Module

### Using AWS Portal
Expand Down Expand Up @@ -197,7 +224,7 @@ Before opening a Pull Request, please do the following:
|------|-------------|------|---------|:--------:|
| <a name="input_automation_account_service_role_name"></a> [automation\_account\_service\_role\_name](#input\_automation\_account\_service\_role\_name) | Name of the custom role in the automation account. See https://docs.meshcloud.io/docs/meshstack.how-to.integrate-meshplatform-aws-manually.html#set-up-aws-account-3-automation | `string` | `"MeshfedAutomationRole"` | no |
| <a name="input_aws_sso_instance_arn"></a> [aws\_sso\_instance\_arn](#input\_aws\_sso\_instance\_arn) | AWS SSO Instance ARN. Needs to be of the form arn:aws:sso:::instance/ssoins-xxxxxxxxxxxxxxx. Setup instructions https://docs.meshcloud.io/docs/meshstack.aws.sso-setup.html. | `string` | n/a | yes |
| <a name="input_can_close_accounts_in_resource_org_paths"></a> [can\_close\_accounts\_in\_resource\_org\_paths](#input\_can\_close\_accounts\_in\_resource\_org\_paths) | AWS ResourceOrgPaths that are used in Landing Zones and where meshStack is allowed to close accounts. | `list(string)` | `[]` | no |
| <a name="input_can_close_accounts_with_tags"></a> [can\_close\_accounts\_with\_tags](#input\_can\_close\_accounts\_with\_tags) | Map of tag keys to lists of tag values that allow account closure. Account closure is opt-in only - if not set or empty, no accounts can be closed. When set, only accounts with matching tags can be closed. Special case: {"\_\_meshstack\_allow\_close\_all\_\_" = ["true"]} allows closing all accounts without restrictions. Example: { "Environment" = ["dev", "staging"], "Team" = ["platform"] } | `map(list(string))` | `{}` | no |
| <a name="input_control_tower_enrollment_enabled"></a> [control\_tower\_enrollment\_enabled](#input\_control\_tower\_enrollment\_enabled) | Set to true, to allow meshStack to enroll Accounts via AWS Control Tower for the meshPlatform. | `bool` | `false` | no |
| <a name="input_control_tower_portfolio_id"></a> [control\_tower\_portfolio\_id](#input\_control\_tower\_portfolio\_id) | Must be set for AWS Control Tower | `string` | `""` | no |
| <a name="input_cost_explorer_management_account_service_role_name"></a> [cost\_explorer\_management\_account\_service\_role\_name](#input\_cost\_explorer\_management\_account\_service\_role\_name) | Name of the custom role in the management account used by the cost explorer user. | `string` | `"MeshCostExplorerServiceRole"` | no |
Expand Down
22 changes: 11 additions & 11 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,17 @@ module "management_account_replicator_access" {
providers = {
aws = aws.management
}
meshcloud_account_id = data.aws_caller_identity.meshcloud.account_id
privileged_external_id = var.replicator_privileged_external_id
support_root_account_via_aws_sso = var.support_root_account_via_aws_sso
aws_sso_instance_arn = var.aws_sso_instance_arn
control_tower_enrollment_enabled = var.control_tower_enrollment_enabled
control_tower_portfolio_id = var.control_tower_portfolio_id
meshcloud_account_service_user_name = var.meshcloud_account_service_user_name
management_account_service_role_name = var.management_account_service_role_name
meshstack_access_role_name = var.meshstack_access_role_name
landing_zone_ou_arns = var.landing_zone_ou_arns
can_close_accounts_in_resource_org_paths = var.can_close_accounts_in_resource_org_paths
meshcloud_account_id = data.aws_caller_identity.meshcloud.account_id
privileged_external_id = var.replicator_privileged_external_id
support_root_account_via_aws_sso = var.support_root_account_via_aws_sso
aws_sso_instance_arn = var.aws_sso_instance_arn
control_tower_enrollment_enabled = var.control_tower_enrollment_enabled
control_tower_portfolio_id = var.control_tower_portfolio_id
meshcloud_account_service_user_name = var.meshcloud_account_service_user_name
management_account_service_role_name = var.management_account_service_role_name
meshstack_access_role_name = var.meshstack_access_role_name
landing_zone_ou_arns = var.landing_zone_ou_arns
can_close_accounts_with_tags = var.can_close_accounts_with_tags

allow_federated_role = var.workload_identity_federation != null

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ No modules.
|------|-------------|------|---------|:--------:|
| <a name="input_allow_federated_role"></a> [allow\_federated\_role](#input\_allow\_federated\_role) | n/a | `bool` | `false` | no |
| <a name="input_aws_sso_instance_arn"></a> [aws\_sso\_instance\_arn](#input\_aws\_sso\_instance\_arn) | ARN of the AWS SSO instance to use | `string` | n/a | yes |
| <a name="input_can_close_accounts_in_resource_org_paths"></a> [can\_close\_accounts\_in\_resource\_org\_paths](#input\_can\_close\_accounts\_in\_resource\_org\_paths) | AWS ResourceOrgPaths that are used in Landing Zones and where meshStack is allowed to close accounts. | `list(string)` | `[]` | no |
| <a name="input_can_close_accounts_with_tags"></a> [can\_close\_accounts\_with\_tags](#input\_can\_close\_accounts\_with\_tags) | Map of tag keys to lists of tag values that allow account closure. Account closure is opt-in only - if not set or empty, no accounts can be closed. When set, only accounts with matching tags can be closed. Special case: {"\_\_meshstack\_allow\_close\_all\_\_" = ["true"]} allows closing all accounts without restrictions. Example: { "Environment" = ["dev", "staging"], "Team" = ["platform"] } | `map(list(string))` | `{}` | no |
| <a name="input_control_tower_enrollment_enabled"></a> [control\_tower\_enrollment\_enabled](#input\_control\_tower\_enrollment\_enabled) | Set to true, to allow meshStack to enroll Accounts via AWS Control Tower for the meshPlatform | `bool` | `false` | no |
| <a name="input_control_tower_portfolio_id"></a> [control\_tower\_portfolio\_id](#input\_control\_tower\_portfolio\_id) | Must be set for AWS Control Tower | `string` | `""` | no |
| <a name="input_landing_zone_ou_arns"></a> [landing\_zone\_ou\_arns](#input\_landing\_zone\_ou\_arns) | Organizational Unit ARNs that are used in Landing Zones. We recommend to explicitly list the OU ARNs that meshStack should manage. | `list(string)` | `[]` | no |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
locals {
account_id = data.aws_caller_identity.current.account_id # current account number.

# Check if unrestricted account closure is enabled using reserved key
allow_close_all_accounts = (
length(var.can_close_accounts_with_tags) == 1 &&
contains(keys(var.can_close_accounts_with_tags), "__meshstack_allow_close_all__") &&
var.can_close_accounts_with_tags["__meshstack_allow_close_all__"][0] == "true"
)

# Get tag restrictions (empty if unrestricted access)
tag_restrictions = local.allow_close_all_accounts ? {} : var.can_close_accounts_with_tags
}

data "aws_caller_identity" "current" {}
Expand Down Expand Up @@ -58,20 +68,32 @@ data "aws_iam_policy_document" "meshfed_service" {
var.landing_zone_ou_arns)
}

statement {
sid = "OrgManagementAccessCloseAccount"
effect = "Allow"
actions = [
"organizations:CloseAccount"
]
resources = [
// allow acting on any account owned by this org
"arn:${data.aws_partition.current.partition}:organizations::*:account/o-*/*",
]
condition {
test = "ForAnyValue:StringLike"
variable = "aws:ResourceOrgPaths"
values = var.can_close_accounts_in_resource_org_paths
# Account closure policy with tag-based conditions:
# Account closure is opt-in only - requires can_close_accounts_with_tags to be explicitly set
# Special case: {"__meshstack_allow_close_all__" = ["true"]} allows closing all accounts without restrictions
# Otherwise: Only accounts with matching tags can be closed
dynamic "statement" {
for_each = length(var.can_close_accounts_with_tags) > 0 ? [1] : []
content {
sid = "OrgManagementAccessCloseAccount"
effect = "Allow"
actions = [
"organizations:CloseAccount"
]
resources = [
// allow acting on any account owned by this org
"arn:${data.aws_partition.current.partition}:organizations::*:account/o-*/*",
]

# Apply tag-based conditions (empty if unrestricted access)
dynamic "condition" {
for_each = local.tag_restrictions
content {
test = "ForAnyValue:StringEquals"
variable = "aws:ResourceTag/${condition.key}"
values = condition.value
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,10 @@ variable "landing_zone_ou_arns" {
default = []
}

variable "can_close_accounts_in_resource_org_paths" {
type = list(string)
// see https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html#condition-keys-resourceorgpaths
description = "AWS ResourceOrgPaths that are used in Landing Zones and where meshStack is allowed to close accounts."
default = [] // example: o-a1b2c3d4e5/r-f6g7h8i9j0example/ou-ghi0-awsccccc/ou-jkl0-awsddddd/
variable "can_close_accounts_with_tags" {
type = map(list(string))
description = "Map of tag keys to lists of tag values that allow account closure. Account closure is opt-in only - if not set or empty, no accounts can be closed. When set, only accounts with matching tags can be closed. Special case: {\"__meshstack_allow_close_all__\" = [\"true\"]} allows closing all accounts without restrictions. Example: { \"Environment\" = [\"dev\", \"staging\"], \"Team\" = [\"platform\"] }"
default = {}
}

variable "allow_federated_role" {
Expand Down
9 changes: 4 additions & 5 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,10 @@ variable "landing_zone_ou_arns" {
default = ["arn:aws:organizations::*:ou/o-*/ou-*"]
}

variable "can_close_accounts_in_resource_org_paths" {
type = list(string)
// see https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html#condition-keys-resourceorgpaths
description = "AWS ResourceOrgPaths that are used in Landing Zones and where meshStack is allowed to close accounts."
default = [] // example: o-a1b2c3d4e5/r-f6g7h8i9j0example/ou-ghi0-awsccccc/ou-jkl0-awsddddd/
variable "can_close_accounts_with_tags" {
type = map(list(string))
description = "Map of tag keys to lists of tag values that allow account closure. Account closure is opt-in only - if not set or empty, no accounts can be closed. When set, only accounts with matching tags can be closed. Special case: {\"__meshstack_allow_close_all__\" = [\"true\"]} allows closing all accounts without restrictions. Example: { \"Environment\" = [\"dev\", \"staging\"], \"Team\" = [\"platform\"] }"
default = {}
}

# ---------------------------------------------------------------------------------------------------------------------
Expand Down
Loading