How To Manage Amazon Inspector in AWS Organizations Using Terraform
Tags: aws, terraform, security
Published: 2024-06-09 07:02:52

## Introduction

Over the past two months, I have published [numerous blog posts on managing different AWS security services in AWS Organizations using Terraform](https://dev.to/acwwat/series/27647). In this blog post, I will cover one remaining AWS service, AWS Inspector, for native vulnerability management. The Terraform resources for Inspector are a bit quirky, so I will show some slightly more advanced techniques to keep the configuration neat and configurable. With that said, let's review the objective.

## About the use case

[Amazon Inspector](https://docs.aws.amazon.com/inspector/latest/user/what-is-inspector.html) is a vulnerability management service that continuously scans AWS workloads for software vulnerabilities and unintended network exposure. Supported compute services include Amazon EC2 instances, container images in Amazon ECR, and AWS Lambda functions.

Similar to other AWS security services, Inspector supports [managing multiple accounts with AWS Organizations](https://docs.aws.amazon.com/inspector/latest/user/managing-multiple-accounts.html) via the delegated administrator feature. Once an account in the organization is designated as a delegated administrator, it can manage member accounts and view aggregated findings.

Since it is increasingly common to establish an AWS landing zone using [AWS Control Tower](https://docs.aws.amazon.com/controltower/latest/userguide/what-is-control-tower.html), we will use the [standard account structure](https://docs.aws.amazon.com/controltower/latest/userguide/accounts.html) in a Control Tower landing zone to demonstrate how to configure Inspector in Terraform:

![Control Tower standard OU and account structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sp8iqdmmqjcl5r4iqyui.png)

The relevant accounts for our use case in the landing zone are:

* The **Management** account for the organization where AWS Organizations is configured. For details, refer to [Managing multiple accounts in Amazon Inspector with Organizations](https://docs.aws.amazon.com/inspector/latest/user/managing-multiple-accounts.html).
    
* The **Audit** account where security and compliance services are typically centralized in a Control Tower landing zone.
    

The objective is to delegate Inspector administrative duties from the **Management** account to the **Audit** account, after which all organization configurations are managed in the **Audit** account. Let's walk through how to do this using Terraform.

## Designating an Inspector administrator account

The Inspector delegated administrator is configured in the **Management** account, so we need a provider associated with it in Terraform. To keep things simple, we will take a multi-provider approach by defining two providers, one for the **Management** account and another for the **Audit** account, using AWS CLI profiles as follows:

```terraform
provider "aws" {
  alias   = "management"
  # Use "aws configure" to create the "management" profile with the Management account credentials
  profile = "management" 
}

provider "aws" {
  alias   = "audit"
  # Use "aws configure" to create the "audit" profile with the Audit account credentials
  profile = "audit" 
}
```

>⚠ Since Inspector is a regional service, you must apply this Terraform configuration on each region that you are using. Consider using the **region** argument in your provider definition and a variable to make your Terraform configuration rerunnable in other regions.

We can designate the delegated administrator using the [`aws_inspector2_delegated_admin_account` resource](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/inspector2_delegated_admin_account). However, this does not enable Inspector in the delegated administrator account, so we also need to use the [`aws_inspector2_enabler` resource](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/inspector2_enabler). What I learned from testing the `aws_inspector2_enabler` resource is that you cannot provide both the delegated account and the member accounts in the `account_ids` argument, so we need a dedicated `aws_inspector2_enabler` resource for the **Audit** account. According to the resource source code, this is to address a legacy Inspector issue.

The resulting Terraform configuration should look like the following (pay special attention to the `provider` argument in each resource):

```terraform
data "aws_caller_identity" "audit" {
  provider = aws.audit
}

resource "aws_inspector2_enabler" "audit" {
  provider       = aws.audit
  account_ids    = [data.aws_caller_identity.audit.account_id]
}

resource "aws_inspector2_delegated_admin_account" "audit" {
  provider   = aws.management
  account_id = data.aws_caller_identity.audit.account_id
  depends_on = [aws_inspector2_enabler.audit]
}
```

## Configuring Inspector activation for new member accounts

To allow more control over which scan types are enabled, we can define the following variables and use them with the relevant resources:

```terraform
# Variable definition (.tfvars)

variable "enable_ec2" {
  description = "Whether Amazon EC2 scans should be enabled for both existing and new member accounts in the organization."
  type        = bool
  default     = true
}

variable "enable_ecr" {
  description = "Whether Amazon ECR scans should be enabled for both existing and new member accounts in the organization."
  type        = bool
  default     = true
}

variable "enable_lambda" {
  description = "Whether Lambda Function scans should be enabled for both existing and new member accounts in the organization."
  type        = bool
  default     = true
}

variable "enable_lambda_code" {
  description = "Whether Lambda code scans should be enabled for both existing and new member accounts in the organization."
  type        = bool
  default     = true
}
```

In an organizational setup, Inspector can auto-enable on new member accounts. In Terraform, this can be configured using the [`aws_inspector2_organization_configuration` resource](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/inspector2_organization_configuration). Leveraging the variables above, the resource can be defined as follows:

```terraform
resource "aws_inspector2_organization_configuration" "this" {
  provider = aws.audit
  auto_enable {
    ec2         = var.enable_ec2
    ecr         = var.enable_ecr
    lambda      = var.enable_lambda
    lambda_code = var.enable_lambda_code && var.enable_lambda
  }
  depends_on = [aws_inspector2_delegated_admin_account.audit]
}
```

Note that for AWS Lambda code scanning (`lambda_code`), AWS Lambda standard scanning (`lambda`) is a prerequisite, so we need to check both variables to enable it.

Now let's address the existing member accounts.

## Activating scanning for existing member accounts

Unlike GuardDuty, the Inspector organization configuration does not support auto-enablement for existing member accounts, so we need to separately manage the member accounts. The strategy is to get the list of *active* member accounts from the organization, which we can use with the Inspector Terraform resources, including the `aws_inspector2_enabler` resource. We can exclude the **Audit** account since that is managed separately. To get the list of member accounts in the organization, we can use the [`aws_organizations_organization` data source](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organization).

Furthermore, the `aws_inspector2_enabler` resource's `resource_types` argument takes a list of strings that represent the scan types to enable. Since the variables we defined earlier are boolean variables, we need a bit of function magic to create the list of scans to enable based on the variables.

The Terraform configuration that addresses the above requirements can be defined as follows:

```terraform
data "aws_organizations_organization" "this" {
  provider = aws.management
}

locals {
  enabler_resource_types = compact([
    var.enable_ec2 ? "EC2" : null,
    var.enable_ecr ? "ECR" : null,
    var.enable_lambda ? "LAMBDA" : null,
    var.enable_lambda_code && var.enable_lambda ? "LAMBDA_CODE" : null,
  ])

  member_account_ids = [for account in data.aws_organizations_organization.this.accounts : account.id if account.status == "ACTIVE" && account.id != data.aws_caller_identity.audit.account_id]
}
```

Member accounts are not automatically associated with the delegated administrator account, so they must first be associated using the [`aws_inspector2_member_association` resource](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/inspector2_member_association).

Using the [`for_each` meta-argument](https://developer.hashicorp.com/terraform/language/meta-arguments/for_each), we can define a single resource to associate all member accounts with the previously defined `member_account_ids` local value:

```terraform
resource "aws_inspector2_member_association" "members" {
  provider   = aws.audit
  for_each   = toset(local.member_account_ids)
  account_id = each.key
  depends_on = [aws_inspector2_delegated_admin_account.audit]
}
```

Lastly, we can enable Inspector scans in the member accounts using the `aws_inspector2_enabler` resource. Although the `account_ids` argument can take the list of member accounts, it is more flexible to have one resource per account. Thus, using `for_each` and the local values, the resource can be defined as follows:

```terraform
resource "aws_inspector2_enabler" "members" {
  provider       = aws.audit
  for_each       = toset(local.member_account_ids)
  account_ids    = [each.key]
  resource_types = local.enabler_resource_types
  depends_on     = [aws_inspector2_member_association.members]
}
```

>✅ You can find the complete Terraform in the [GitHub repository](https://github.com/acwwat/terraform-amazon-inspector-organization-example) that accompanies this blog post.

Now that the Terraform configuration is fully defined, you can apply it to establish the **Audit** account as the delegated administration and centrally manage Inspector settings for both new and existing accounts.

## Caveats about deactivating Inspector in member accounts

Among the AWS security services, Inspector has the least sophisticated API for organizational management. The mix between auto-enablement for new member accounts and explicit enablement for existing member accounts complicates how they are managed in Terraform, particularly if you are trying to disable Inspector via `terraform destroy`.

Consider the case where a new member account is added and auto-enablement is applied to this account. If you run `terraform destroy` as-is, Terraform is not aware of the new member account, and thus Inspector cannot be deactivated in this account. You must manually deactivate the account in each applied region.

Alternatively, you can first run `terraform apply` so that the `aws_inspector2_member_association` and `aws_inspector2_enabler` resource instances are created, then run `terraform destroy` to properly clean up. While this method works, you must keep track of when new member accounts are added so that you know when to run `terraform apply` to reconcile the Terraform resources with the updated organization.

In any case, be aware of this caveat and take one of the two approaches if you ever need to clean up Inspector resources.

## Summary

In this blog post, you learned how to manage Amazon Inspector in AWS Organizations using Terraform. With a delegated administrator, Inspector can be auto-enabled for new member accounts, while existing member accounts are dynamically associated and configured with the desired scan types. If you have also [configured AWS Security Hub to operate at the organization level](https://dev.to/aws-builders/how-to-manage-aws-security-hub-in-aws-organizations-using-terraform-5gl4), you can manage Inspector findings across accounts and regions, thereby streamlining your security operations.

If you are interested in this type of content, be sure to read other posts on the [Avangards Blog](https://blog.avangards.io), where I share tips and deep dives on AWS, Terraform, and beyond. Thank you, and enjoy the rest of your day!