How To Manage Amazon Inspector in AWS Organizations Using Terraform
Introduction
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 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 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, we will use the standard account structure in a Control Tower landing
zone to demonstrate how to configure Inspector in Terraform:
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.
- 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:
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.
However, this does not enable Inspector in the delegated administrator account,
so we also need to use the aws_inspector2_enabler resource. 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):
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:
# 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.
Leveraging the variables above, the resource can be defined as follows:
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.
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:
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.
Using the for_each meta-argument, we can define a single
resource to associate all member accounts with the previously defined member_account_ids local
value:
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:
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]
}
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.
Thant Zin Phyo@Cracky (MCT, MCE, MVP, AWS CB)
Comments
Post a Comment