From 156b9e554ed975604ed6c05ece23fe3915ad6230 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Thu, 20 Mar 2025 22:02:09 +0000 Subject: [PATCH 1/4] Introduce Elastio Asset Account CloudFormation StackSet --- .github/ci.yml | 47 ++++++ .gitignore | 4 + asset-account/README.md | 11 ++ asset-account/terraform/stack-set/README.md | 7 + .../examples/self-managed/.terraform.lock.hcl | 25 +++ .../stack-set/examples/self-managed/README.md | 7 + .../stack-set/examples/self-managed/admin.tf | 68 ++++++++ .../stack-set/examples/self-managed/asset.tf | 35 ++++ .../examples/self-managed/providers.tf | 22 +++ .../examples/self-managed/variables.tf | 23 +++ asset-account/terraform/stack-set/main.tf | 99 +++++++++++ asset-account/terraform/stack-set/outputs.tf | 14 ++ .../terraform/stack-set/variables.tf | 157 ++++++++++++++++++ asset-account/terraform/stack-set/versions.tf | 10 ++ 14 files changed, 529 insertions(+) create mode 100644 .github/ci.yml create mode 100644 .gitignore create mode 100644 asset-account/README.md create mode 100644 asset-account/terraform/stack-set/README.md create mode 100644 asset-account/terraform/stack-set/examples/self-managed/.terraform.lock.hcl create mode 100644 asset-account/terraform/stack-set/examples/self-managed/README.md create mode 100644 asset-account/terraform/stack-set/examples/self-managed/admin.tf create mode 100644 asset-account/terraform/stack-set/examples/self-managed/asset.tf create mode 100644 asset-account/terraform/stack-set/examples/self-managed/providers.tf create mode 100644 asset-account/terraform/stack-set/examples/self-managed/variables.tf create mode 100644 asset-account/terraform/stack-set/main.tf create mode 100644 asset-account/terraform/stack-set/outputs.tf create mode 100644 asset-account/terraform/stack-set/variables.tf create mode 100644 asset-account/terraform/stack-set/versions.tf diff --git a/.github/ci.yml b/.github/ci.yml new file mode 100644 index 0000000..80d5307 --- /dev/null +++ b/.github/ci.yml @@ -0,0 +1,47 @@ +name: ci + +on: + push: + branches: [master] + pull_request: + +jobs: + typos: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: crate-ci/typos@v1.30.2 + + terraform-fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: hashicorp/setup-terraform@v1 + with: + terraform_version: ^1 + terraform_wrapper: false + + - run: terraform fmt -check + + terraform-validate: + runs-on: ubuntu-latest + + strategy: + matrix: + project: + - asset-account/terraform/stack-set/examples/basic + + steps: + - uses: actions/checkout@v4 + + - uses: hashicorp/setup-terraform@v1 + with: + terraform_version: ^1 + terraform_wrapper: false + + - run: terraform init -input=false + working-directory: ${{ matrix.project }} + + - run: terraform validate + working-directory: ${{ matrix.project }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e911d17 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.terraform +terraform.tfstate +terraform.tfstate.backup +terraform.tfvars diff --git a/asset-account/README.md b/asset-account/README.md new file mode 100644 index 0000000..9af3f54 --- /dev/null +++ b/asset-account/README.md @@ -0,0 +1,11 @@ +# Elastio Asset Account Stack + +Elastio Asset Account stack creates IAM roles to link the AWS account with the Elastio Connector. This allows the Elastio Connector to scan the assets available in the account where the Elastio Asset Account stack is deployed. + +There are several ways to deploy the Elastio Asset Account stack, that we'll review below. + +## AWS CloudFormation StackSet + +You can generate the CloudFormation template link for your Elastio Asset Account using the Elastio Portal UI and then deploy it via a CloudFormation StackSet either manually, or using the Elastio official Terraform wrapper module for this. + +See the [`terraform/stack-set`](./terraform/stack-set) to get started. diff --git a/asset-account/terraform/stack-set/README.md b/asset-account/terraform/stack-set/README.md new file mode 100644 index 0000000..ac9d8d0 --- /dev/null +++ b/asset-account/terraform/stack-set/README.md @@ -0,0 +1,7 @@ +# Elastio Asset Account CloudFormation StackSet + +See [this README](../..) for more details on what this stack does. + +This is a Terraform module, that is a thin wrapper on top of an [`aws_cloudformation_stack_set`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set) and [`aws_cloudformation_stack_instances`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_instances) resources used to deploy the Elastio Asset Account stack. + +See the `examples` directory for some examples of how this module can be used. diff --git a/asset-account/terraform/stack-set/examples/self-managed/.terraform.lock.hcl b/asset-account/terraform/stack-set/examples/self-managed/.terraform.lock.hcl new file mode 100644 index 0000000..7de3aa8 --- /dev/null +++ b/asset-account/terraform/stack-set/examples/self-managed/.terraform.lock.hcl @@ -0,0 +1,25 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.92.0" + constraints = ">= 5.0.0" + hashes = [ + "h1:ZnpTxMfg5PThZc5WZCsZELinsR0gPhdTpNmXjVcf7aE=", + "zh:1d3a0b40831360e8e988aee74a9ff3d69d95cb541c2eae5cb843c64303a091ba", + "zh:3d29cbced6c708be2041a708d25c7c0fc22d09e4d0b174360ed113bfae786137", + "zh:4341a203cf5820a0ca18bb514ae10a6c113bc6a728fb432acbf817d232e8eff4", + "zh:4a49e2d91e4d92b6b93ccbcbdcfa2d67935ce62e33b939656766bb81b3fd9a2c", + "zh:54c7189358b37fd895dedbabf84e509c1980a8c404a1ee5b29b06e40497b8655", + "zh:5d8bb1ff089c37cb65c83b4647f1981fded993e87d8132915d92d79f29e2fcd8", + "zh:618f2eb87cd65b245aefba03991ad714a51ff3b841016ef68e2da2b85d0b2325", + "zh:7bce07bc542d0588ca42bac5098dd4f8af715417cd30166b4fb97cedd44ab109", + "zh:81419eab2d8810beb114b1ff5cbb592d21edc21b809dc12bb066e4b88fdd184a", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9dea39d4748eeeebe2e76ca59bca4ccd161c2687050878c47289a98407a23372", + "zh:d692fc33b67ac89e916c8f9233d39eacab8c438fe10172990ee9d94fba5ca372", + "zh:d9075c7da48947c029ba47d5985e1e8e3bf92367bfee8ca1ff0e747765e779a1", + "zh:e81c62db317f3b640b2e04eba0ada8aa606bcbae0152c09f6242e86b86ef5889", + "zh:f68562e073722c378d2f3529eb80ad463f12c44aa5523d558ae3b69f4de5ca1f", + ] +} diff --git a/asset-account/terraform/stack-set/examples/self-managed/README.md b/asset-account/terraform/stack-set/examples/self-managed/README.md new file mode 100644 index 0000000..0d7cb3a --- /dev/null +++ b/asset-account/terraform/stack-set/examples/self-managed/README.md @@ -0,0 +1,7 @@ +# Self-Managed StackSet Example + +This is a basic example of using the `elastio-asset-account-stack-set` terraform module with the self-managed AWS Cloudformation StackSet. + +You can deploy it even within a single account. Just specify the `template_url` input variable at minimum. + +You can specify the `admin_account_aws_profile` and `asset_account_aws_profile` to use separate Admin and Asset accounts. If you don't specify them, then the default AWS account configured in your environment will be used as both the Admin and the Asset account. diff --git a/asset-account/terraform/stack-set/examples/self-managed/admin.tf b/asset-account/terraform/stack-set/examples/self-managed/admin.tf new file mode 100644 index 0000000..9c41592 --- /dev/null +++ b/asset-account/terraform/stack-set/examples/self-managed/admin.tf @@ -0,0 +1,68 @@ +module "elastio_asset_accounts" { + # Use the link from the real terraform registry here. Relative path is used for testing purposes. + source = "../../" + providers = { + aws = aws.admin + } + + # Needs to be deployed only after the execution role in the asset account is created + depends_on = [aws_iam_role.execution] + + template_url = var.template_url + + # we are deploying just into a single asset account in this example + accounts = [local.asset_account_id] + + stack_set = { + administration_role_arn = aws_iam_role.admin.arn + } +} + +# Admin role, that StackSets will use to access the asset accounts to deploy the stacks +resource "aws_iam_role" "admin" { + provider = aws.admin + + assume_role_policy = data.aws_iam_policy_document.admin_trust.json + name = "AWSCloudFormationStackSetAdministrationRole" +} + +data "aws_iam_policy_document" "admin_trust" { + statement { + actions = ["sts:AssumeRole"] + effect = "Allow" + + principals { + identifiers = ["cloudformation.amazonaws.com"] + type = "Service" + } + + # Conditions to prevent the confused deputy attack + condition { + test = "StringEquals" + variable = "aws:SourceAccount" + values = [local.admin_account_id] + } + + condition { + test = "StringLike" + variable = "aws:SourceArn" + values = ["arn:aws:cloudformation:*:${local.admin_account_id}:stackset/*"] + } + } +} + +data "aws_iam_policy_document" "admin_execution" { + statement { + actions = ["sts:AssumeRole"] + effect = "Allow" + resources = ["arn:aws:iam::*:role/AWSCloudFormationStackSetExecutionRole"] + } +} + +resource "aws_iam_role_policy" "admin_execution" { + provider = aws.admin + + name = "AssumeExecutionRole" + policy = data.aws_iam_policy_document.admin_execution.json + role = aws_iam_role.admin.name +} diff --git a/asset-account/terraform/stack-set/examples/self-managed/asset.tf b/asset-account/terraform/stack-set/examples/self-managed/asset.tf new file mode 100644 index 0000000..baff615 --- /dev/null +++ b/asset-account/terraform/stack-set/examples/self-managed/asset.tf @@ -0,0 +1,35 @@ +resource "aws_iam_role" "execution" { + provider = aws.asset + + name = "AWSCloudFormationStackSetExecutionRole" + assume_role_policy = data.aws_iam_policy_document.execution_trust.json +} + +data "aws_iam_policy_document" "execution_trust" { + statement { + actions = ["sts:AssumeRole"] + effect = "Allow" + + principals { + identifiers = [aws_iam_role.admin.arn] + type = "AWS" + } + } +} + +# Specifies the set of permissions required for the deployment of the Cloudfomation stack +data "aws_iam_policy_document" "execution_deployment" { + statement { + actions = ["*"] + effect = "Allow" + resources = ["*"] + } +} + +resource "aws_iam_role_policy" "execution_deployment" { + provider = aws.asset + + name = "Deployment" + policy = data.aws_iam_policy_document.execution_deployment.json + role = aws_iam_role.execution.name +} diff --git a/asset-account/terraform/stack-set/examples/self-managed/providers.tf b/asset-account/terraform/stack-set/examples/self-managed/providers.tf new file mode 100644 index 0000000..10751be --- /dev/null +++ b/asset-account/terraform/stack-set/examples/self-managed/providers.tf @@ -0,0 +1,22 @@ +provider "aws" { + alias = "admin" + profile = var.admin_account_aws_profile +} + +provider "aws" { + alias = "asset" + profile = var.asset_account_aws_profile +} + +data "aws_caller_identity" "admin" { + provider = aws.admin +} + +data "aws_caller_identity" "asset" { + provider = aws.asset +} + +locals { + admin_account_id = data.aws_caller_identity.admin.account_id + asset_account_id = data.aws_caller_identity.asset.account_id +} diff --git a/asset-account/terraform/stack-set/examples/self-managed/variables.tf b/asset-account/terraform/stack-set/examples/self-managed/variables.tf new file mode 100644 index 0000000..e355baf --- /dev/null +++ b/asset-account/terraform/stack-set/examples/self-managed/variables.tf @@ -0,0 +1,23 @@ +variable "template_url" { + description = <<-DESCR + The URL of the Elastio Asset Account CloudFormation template obtained from + the Elastio Portal. + + This parameter is sensitive, because anyone who knows this URL can deploy + Elastio Account stack and linking it to your Elastio tenant. + DESCR + + sensitive = true + type = string + nullable = false +} + +variable "admin_account_aws_profile" { + type = string + default = null +} + +variable "asset_account_aws_profile" { + type = string + default = null +} diff --git a/asset-account/terraform/stack-set/main.tf b/asset-account/terraform/stack-set/main.tf new file mode 100644 index 0000000..8845cb6 --- /dev/null +++ b/asset-account/terraform/stack-set/main.tf @@ -0,0 +1,99 @@ + +resource "aws_cloudformation_stack_set" "this" { + name = lookup(var.stack_set, "name", "ElastioAssetAccount") + description = lookup(var.stack_set, "description", + <<-DESCR + Elastio Asset Account StackSet creates IAM roles to link the AWS accounts with + the Elastio Connector. This allows the Elastio Connector to scan the assets + available in the account where the Elastio Asset Account stack instances are + deployed. + DESCR + ) + + administration_role_arn = lookup(var.stack_set, "administration_role_arn", null) + + dynamic "auto_deployment" { + for_each = lookup(var.stack_set, "auto_deployment", []) + content { + enabled = auto_deployment.value.enabled + retain_stacks_on_account_removal = auto_deployment.value.retain_stacks_on_account_removal + } + } + + capabilities = ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"] + execution_role_name = lookup(var.stack_set, "execution_role_name", null) + + dynamic "managed_execution" { + for_each = lookup(var.stack_set, "managed_execution", []) + content { + active = managed_execution.value.active + } + } + + dynamic "operation_preferences" { + for_each = var.operation_preferences[*] + content { + failure_tolerance_count = operation_preferences.value.failure_tolerance_count + failure_tolerance_percentage = operation_preferences.value.failure_tolerance_percentage + max_concurrent_count = operation_preferences.value.max_concurrent_count + max_concurrent_percentage = operation_preferences.value.max_concurrent_percentage + } + } + + parameters = { + for key, value in { + iamResourceNamesPrefix = var.iam_resource_names_prefix + iamResourceNamesSuffix = var.iam_resource_names_suffix + encryptWithCmk = var.encrypt_with_cmk + lambdaTracing = var.lambda_tracing + } : + key => tostring(value) + } + + permission_model = lookup(var.stack_set, "permission_model", null) + call_as = lookup(var.stack_set, "call_as", null) + tags = merge( + var.tags, + lookup(var.stack_set, "tags", {}), + { + "elastio:resource" = true + }, + ) + + template_url = var.template_url + + # Ignore some internal parameter values + lifecycle { + ignore_changes = [ + parameters["cloudConnectorAccountId"], + parameters["cloudConnectorRoleExternalId"], + parameters["deploymentNotificationToken"], + parameters["deploymentNotificationTopicArn"], + ] + } +} + +resource "aws_cloudformation_stack_instances" "this" { + stack_set_name = aws_cloudformation_stack_set.this.name + + accounts = var.accounts + + dynamic "deployment_targets" { + for_each = var.deployment_targets[*] + content { + account_filter_type = deployment_targets.value.account_filter_type + accounts = deployment_targets.value.accounts + accounts_url = deployment_targets.value.accounts_url + organizational_unit_ids = deployment_targets.value.organizational_unit_ids + } + } + + dynamic "operation_preferences" { + for_each = var.operation_preferences[*] + content { + concurrency_mode = operation_preferences.concurrency_mode + } + } + + retain_stacks = lookup(var.stack_instances, "retain_stacks", null) +} diff --git a/asset-account/terraform/stack-set/outputs.tf b/asset-account/terraform/stack-set/outputs.tf new file mode 100644 index 0000000..7617831 --- /dev/null +++ b/asset-account/terraform/stack-set/outputs.tf @@ -0,0 +1,14 @@ + +output "stack_set" { + value = aws_cloudformation_stack_set.this + description = < Date: Thu, 20 Mar 2025 23:08:14 +0000 Subject: [PATCH 2/4] Add service-managed example, and refactor --- .gitignore | 1 + asset-account/terraform/stack-set/README.md | 4 +- .../stack-set/examples/self-managed/admin.tf | 17 +-- .../service-managed/.terraform.lock.hcl | 25 ++++ .../examples/service-managed/README.md | 9 ++ .../examples/service-managed/main.tf | 16 +++ .../examples/service-managed/variables.tf | 30 +++++ asset-account/terraform/stack-set/main.tf | 42 +++---- .../terraform/stack-set/variables.tf | 109 +++++++++++++----- 9 files changed, 186 insertions(+), 67 deletions(-) create mode 100644 asset-account/terraform/stack-set/examples/service-managed/.terraform.lock.hcl create mode 100644 asset-account/terraform/stack-set/examples/service-managed/README.md create mode 100644 asset-account/terraform/stack-set/examples/service-managed/main.tf create mode 100644 asset-account/terraform/stack-set/examples/service-managed/variables.tf diff --git a/.gitignore b/.gitignore index e911d17..f4c18be 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .terraform +.terraform.tfstate.lock.info terraform.tfstate terraform.tfstate.backup terraform.tfvars diff --git a/asset-account/terraform/stack-set/README.md b/asset-account/terraform/stack-set/README.md index ac9d8d0..d1a38ae 100644 --- a/asset-account/terraform/stack-set/README.md +++ b/asset-account/terraform/stack-set/README.md @@ -4,4 +4,6 @@ See [this README](../..) for more details on what this stack does. This is a Terraform module, that is a thin wrapper on top of an [`aws_cloudformation_stack_set`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set) and [`aws_cloudformation_stack_instances`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_instances) resources used to deploy the Elastio Asset Account stack. -See the `examples` directory for some examples of how this module can be used. +See the `examples` directory for some examples of how this module can be used: +- `self-managed` - deploy the stack set using the [self-managed permission model](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacksets-getting-started-create-self-managed.html) +- `service-managed` - deploy the stack set using the [service-managed permission model](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacksets-orgs-associate-stackset-with-org.html) diff --git a/asset-account/terraform/stack-set/examples/self-managed/admin.tf b/asset-account/terraform/stack-set/examples/self-managed/admin.tf index 9c41592..c9c821e 100644 --- a/asset-account/terraform/stack-set/examples/self-managed/admin.tf +++ b/asset-account/terraform/stack-set/examples/self-managed/admin.tf @@ -5,17 +5,20 @@ module "elastio_asset_accounts" { aws = aws.admin } - # Needs to be deployed only after the execution role in the asset account is created - depends_on = [aws_iam_role.execution] + depends_on = [ + # Needs to wait for the execution role in the asset account to be fully created + aws_iam_role_policy.execution_deployment, + + # Needs to wait for the admin role in the admin account to be fully created + aws_iam_role_policy.admin_execution, + ] template_url = var.template_url - # we are deploying just into a single asset account in this example - accounts = [local.asset_account_id] + # We are deploying just into a single asset account in this example + accounts = [local.asset_account_id] - stack_set = { - administration_role_arn = aws_iam_role.admin.arn - } + administration_role_arn = aws_iam_role.admin.arn } # Admin role, that StackSets will use to access the asset accounts to deploy the stacks diff --git a/asset-account/terraform/stack-set/examples/service-managed/.terraform.lock.hcl b/asset-account/terraform/stack-set/examples/service-managed/.terraform.lock.hcl new file mode 100644 index 0000000..7de3aa8 --- /dev/null +++ b/asset-account/terraform/stack-set/examples/service-managed/.terraform.lock.hcl @@ -0,0 +1,25 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.92.0" + constraints = ">= 5.0.0" + hashes = [ + "h1:ZnpTxMfg5PThZc5WZCsZELinsR0gPhdTpNmXjVcf7aE=", + "zh:1d3a0b40831360e8e988aee74a9ff3d69d95cb541c2eae5cb843c64303a091ba", + "zh:3d29cbced6c708be2041a708d25c7c0fc22d09e4d0b174360ed113bfae786137", + "zh:4341a203cf5820a0ca18bb514ae10a6c113bc6a728fb432acbf817d232e8eff4", + "zh:4a49e2d91e4d92b6b93ccbcbdcfa2d67935ce62e33b939656766bb81b3fd9a2c", + "zh:54c7189358b37fd895dedbabf84e509c1980a8c404a1ee5b29b06e40497b8655", + "zh:5d8bb1ff089c37cb65c83b4647f1981fded993e87d8132915d92d79f29e2fcd8", + "zh:618f2eb87cd65b245aefba03991ad714a51ff3b841016ef68e2da2b85d0b2325", + "zh:7bce07bc542d0588ca42bac5098dd4f8af715417cd30166b4fb97cedd44ab109", + "zh:81419eab2d8810beb114b1ff5cbb592d21edc21b809dc12bb066e4b88fdd184a", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9dea39d4748eeeebe2e76ca59bca4ccd161c2687050878c47289a98407a23372", + "zh:d692fc33b67ac89e916c8f9233d39eacab8c438fe10172990ee9d94fba5ca372", + "zh:d9075c7da48947c029ba47d5985e1e8e3bf92367bfee8ca1ff0e747765e779a1", + "zh:e81c62db317f3b640b2e04eba0ada8aa606bcbae0152c09f6242e86b86ef5889", + "zh:f68562e073722c378d2f3529eb80ad463f12c44aa5523d558ae3b69f4de5ca1f", + ] +} diff --git a/asset-account/terraform/stack-set/examples/service-managed/README.md b/asset-account/terraform/stack-set/examples/service-managed/README.md new file mode 100644 index 0000000..4af9f91 --- /dev/null +++ b/asset-account/terraform/stack-set/examples/service-managed/README.md @@ -0,0 +1,9 @@ +# Service-Managed StackSet Example + +This is a basic example of using the `elastio-asset-account-stack-set` terraform module with the service-managed AWS Cloudformation StackSet. + +You'll need to deploy it from the AWS Management account. You'll also need to specify both the input variables: `accounts` and `organizational_unit_ids`. + +AWS API requires at least one org unit ID that contains the provided accounts. It doesn't mean you'll deploy the StackSet into the entire org unit, it's just a quirk of the AWS API. The Stack set instances will still be deployed into the accounts specified in `accounts`. + +If you want to deploy into the entire org unit, then modify the `deployment_targets` as needed for your use case. diff --git a/asset-account/terraform/stack-set/examples/service-managed/main.tf b/asset-account/terraform/stack-set/examples/service-managed/main.tf new file mode 100644 index 0000000..9d85a5e --- /dev/null +++ b/asset-account/terraform/stack-set/examples/service-managed/main.tf @@ -0,0 +1,16 @@ +module "elastio_asset_accounts" { + # Use the link from the real terraform registry here. Relative path is used for testing purposes. + source = "../../" + + template_url = var.template_url + + permission_model = "SERVICE_MANAGED" + deployment_targets = { + account_filter_type = "INTERSECTION" + accounts = var.accounts + organizational_unit_ids = var.organizational_unit_ids + } + auto_deployment = { + enabled = false + } +} diff --git a/asset-account/terraform/stack-set/examples/service-managed/variables.tf b/asset-account/terraform/stack-set/examples/service-managed/variables.tf new file mode 100644 index 0000000..7f01719 --- /dev/null +++ b/asset-account/terraform/stack-set/examples/service-managed/variables.tf @@ -0,0 +1,30 @@ +variable "template_url" { + description = <<-DESCR + The URL of the Elastio Asset Account CloudFormation template obtained from + the Elastio Portal. + + This parameter is sensitive, because anyone who knows this URL can deploy + Elastio Account stack and linking it to your Elastio tenant. + DESCR + + sensitive = true + type = string + nullable = false +} + +variable "accounts" { + type = list(string) + + description = <<-DESCR + List of AWS account IDs where the Elastio Asset Account stack instances will + be deployed. + DESCR +} + +variable "organizational_unit_ids" { + type = list(string) + + description = <<-DESCR + Organization root ID or organizational unit (OU) IDs to which stack sets deploy. + DESCR +} diff --git a/asset-account/terraform/stack-set/main.tf b/asset-account/terraform/stack-set/main.tf index 8845cb6..91fe644 100644 --- a/asset-account/terraform/stack-set/main.tf +++ b/asset-account/terraform/stack-set/main.tf @@ -1,30 +1,26 @@ - resource "aws_cloudformation_stack_set" "this" { - name = lookup(var.stack_set, "name", "ElastioAssetAccount") - description = lookup(var.stack_set, "description", - <<-DESCR - Elastio Asset Account StackSet creates IAM roles to link the AWS accounts with - the Elastio Connector. This allows the Elastio Connector to scan the assets - available in the account where the Elastio Asset Account stack instances are - deployed. - DESCR - ) + tags = merge(var.tags, { "elastio:resource" = true }) + + name = var.stack_set_name + description = var.stack_set_description + administration_role_arn = var.administration_role_arn + execution_role_name = var.execution_role_name + permission_model = var.permission_model + call_as = var.call_as + template_url = var.template_url - administration_role_arn = lookup(var.stack_set, "administration_role_arn", null) + capabilities = ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"] dynamic "auto_deployment" { - for_each = lookup(var.stack_set, "auto_deployment", []) + for_each = var.auto_deployment[*] content { enabled = auto_deployment.value.enabled retain_stacks_on_account_removal = auto_deployment.value.retain_stacks_on_account_removal } } - capabilities = ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"] - execution_role_name = lookup(var.stack_set, "execution_role_name", null) - dynamic "managed_execution" { - for_each = lookup(var.stack_set, "managed_execution", []) + for_each = var.managed_execution[*] content { active = managed_execution.value.active } @@ -50,18 +46,6 @@ resource "aws_cloudformation_stack_set" "this" { key => tostring(value) } - permission_model = lookup(var.stack_set, "permission_model", null) - call_as = lookup(var.stack_set, "call_as", null) - tags = merge( - var.tags, - lookup(var.stack_set, "tags", {}), - { - "elastio:resource" = true - }, - ) - - template_url = var.template_url - # Ignore some internal parameter values lifecycle { ignore_changes = [ @@ -95,5 +79,5 @@ resource "aws_cloudformation_stack_instances" "this" { } } - retain_stacks = lookup(var.stack_instances, "retain_stacks", null) + retain_stacks = var.retain_stacks } diff --git a/asset-account/terraform/stack-set/variables.tf b/asset-account/terraform/stack-set/variables.tf index b3a02a0..6699cae 100644 --- a/asset-account/terraform/stack-set/variables.tf +++ b/asset-account/terraform/stack-set/variables.tf @@ -18,10 +18,10 @@ variable "template_url" { variable "accounts" { description = <<-DESCR - List of AWS account IDs where the Elastio Asset Account stack instances will - be deployed. + The IDs AWS accounts where you want to create stack instances. - You can specify `accounts` or `deployment_targets`, but not both. + Specify `accounts` only if you are using `SELF_MANAGED` permissions model. + If you are using the `SERVICE_MANAGED` permissions model specify `deployment_targets` instead. DESCR type = list(string) @@ -30,16 +30,20 @@ variable "accounts" { variable "deployment_targets" { description = <<-DESCR - More flexible way to specify the accounts where the Elastio Asset Account stack. - This is passed directly as a parameter to the `aws_cloudformation_stack_instances` - resource. + The AWS Organizations accounts for which to create stack instances. - You can specify `accounts` or `deployment_targets`, but not both. + Specify `deployment_targets` only if you are using `SERVICE_MANAGED` permissions model. + If you are using the `SELF_MANAGED` permissions model specify `accounts` instead. Details: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_instances#deployment_targets DESCR - type = any + type = object({ + account_filter_type = optional(string) + accounts = optional(list(string)) + accounts_url = optional(string) + organizational_unit_ids = optional(list(string)) + }) default = null } @@ -66,12 +70,38 @@ variable "tags" { default = {} } -variable "operation_preferences" { - description = <<-DESCR - Preferences for how AWS CloudFormation performs a stack set operation. +variable "auto_deployment" { + type = object({ + enabled = optional(bool) + retain_stacks_on_account_removal = optional(bool) + }) + + default = null +} + +variable "stack_set_name" { + type = string + nullable = false + default = "ElastioAssetAccount" +} - Details: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_instances#operation_preferences +variable "stack_set_description" { + type = string + nullable = false + default = <<-DESCR + Elastio Asset Account StackSet creates IAM roles to link the AWS accounts with + the Elastio Connector. This allows the Elastio Connector to scan the assets + available in the account where the Elastio Asset Account stack instances are + deployed. DESCR +} + +################################## +## Deployment execution options ## +################################## + +variable "operation_preferences" { + description = "See https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_instances#operation_preferences" type = object({ concurrency_mode = optional(string) @@ -84,30 +114,49 @@ variable "operation_preferences" { }) default = null } -variable "stack_set" { - description = <<-DESCR - Additional configurations override for the aws_cloudformation_stack_set resource. - These parameters will be forwarded to the resource as-is. See the module's source - code to see what parameters may be passed here. - Details: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set - DESCR +variable "managed_execution" { + description = "See https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set#managed_execution-1" - type = any - default = {} + type = object({ + active = optional(bool) + }) + default = null } -variable "stack_instances" { - description = <<-DESCR - Additional configurations override for the aws_cloudformation_stack_instances resource. - These parameters will be forwarded to the resource as-is. See the module's source - code to see what parameters may be passed here. +variable "administration_role_arn" { + description = "See https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set#administration_role_arn-1" - Details: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_instances - DESCR + type = string + default = null +} - type = any - default = {} +variable "execution_role_name" { + description = "See https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set#execution_role_name-1" + + type = string + default = null +} + +variable "permission_model" { + description = "See https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set#permission_model-1" + + type = string + default = null +} + +variable "call_as" { + description = "See https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set#call_as-1" + + type = string + default = null +} + +variable "retain_stacks" { + description = "See https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_instances#retain_stacks-1" + + type = bool + default = null } ###################################################### From 54bffde33c5d5080898f0c4f468d75a7854a7a41 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Thu, 20 Mar 2025 23:10:37 +0000 Subject: [PATCH 3/4] Fix github workflow location --- .github/{ => workflows}/ci.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{ => workflows}/ci.yml (100%) diff --git a/.github/ci.yml b/.github/workflows/ci.yml similarity index 100% rename from .github/ci.yml rename to .github/workflows/ci.yml From 2bc543714fbf682d2f49fdf52a0e6dfc8069cbf4 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Thu, 20 Mar 2025 23:11:20 +0000 Subject: [PATCH 4/4] Fix CI --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80d5307..dc36be4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,8 @@ jobs: strategy: matrix: project: - - asset-account/terraform/stack-set/examples/basic + - asset-account/terraform/stack-set/examples/self-managed + - asset-account/terraform/stack-set/examples/service-managed steps: - uses: actions/checkout@v4