Skip to content

Commit

Permalink
Merge pull request #173 from cahnk/macie-rewite
Browse files Browse the repository at this point in the history
  • Loading branch information
lorengordon authored May 20, 2022
2 parents e629408 + efb220e commit a0864f3
Show file tree
Hide file tree
Showing 25 changed files with 1,172 additions and 103 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.0.1
current_version = 1.0.0
commit = True
message = Bumps version to {new_version}
tag = False
Expand Down
13 changes: 11 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: minimal

stages:
- lint
# - test
- deploy

if: branch = master OR type = pull_request
Expand All @@ -21,6 +22,15 @@ jobs:
- stage: lint
name: Project Syntax Verification
script: make docker/run target=lint
# moto has not implemented Macie configurations yet, so the CI test fails while running pytest. The tests pass when run against an actual AWS account. We need to uncomment the following lines when moto catches up.
#- stage: test
# name: Apply Terraform test configs in mockstack
# install:
# - make docker-compose/install
# - make mockstack/up
# script: make mockstack/pytest PYTEST_ARGS="-k 'not create_macie_member and not create_macie_org_admin_account'"
# after_script:
# - make mockstack/clean
- stage: deploy
if: branch = master AND type = push AND repo = plus3it/terraform-aws-tardigrade-macie-member
before_script:
Expand All @@ -35,8 +45,7 @@ jobs:
(set -x; git tag -a $RELEASE_VERSION -m $RELEASE_VERSION)
deploy:
provider: releases
api_key:
secure: KONm2zUpJO5nXEdW/Ql1VLHtPh0C4TKUPIBz+EFdMDHVFhBRiQrV+LKU4mLaNEY3cj3xkdTyxfDqpcovwP9xlC7oB0v6EsJgVrbeK+IE1TnDa+Xn/1XQ/b6p7+KfWc2BsooJEuLQiI6BB52kV89TbayYV+UaDqUTwuhz1RNkRcvrCyYI5m9GwA+8hIHwfs7em76b0HA38aPt0OhJAEjBBd1mshnTvnZTNdbFpK7t0crIrwaiKszVkTU5JWRf4xUumcJqF5W6oszKvAMutxSSgcHSZgdZajWbmZ9xDi3w6WCjcUU6L7VJTLbGovmps9EK8ZsDRwzhjK46TKV7kvtgoi3y26VXeHF8qJF1qIg9n03RGuzz084iuI74MyY+7kv16XB4TX1pjpa45Y336u75QY13ysOlGfsAZUfjABhcV6jZacrIRixr8+013jNdZOw3K3BBiVzSXowihJ4n4dAU9ohrYf2LqLDan4XbSjmFY/NxloGS7Y1rZtP1G49KJwMlhemyYH/9W3q1+uZtEcG8Kd6Ek6hSBACajegAYcSQndNdj5tghoOcDt/DHPh0A+qdyay4+yqsqXAqJIgWlcmfCs6vZFrNxYx1pUnsBQDuZ5MIybcrkjVonCEc2YCj+LwZ0hURRgltX7nEMGUnaLd7GgJLG4pZp9Tqj4KiYTMvMW0=
api_key: $GH_RELEASES_TOKEN
name: $RELEASE_VERSION
body: $RELEASE_BODY
tag_name: $RELEASE_VERSION
Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).

### 1.0.0

**Released**: 2022.05.20

**Commit Delta**: [Change from 0.0.1 release](https://github.com/plus3it/terraform-aws-tardigrade-macie-member/compare/0.0.1...1.0.0)

**Summary**:

* Rewrite of module using terraform Macie2. This main module creates a standard Macie configuration in a single AWS account. These include a Macie classification_job, findings_filter, and custom_data_identifier. Macie configurations that require multiple AWS accounts are not included in this module, and the terraform code for those configurations has been implemented in seperate submodeles. There are submodules for a Macie member and a Macie org admin (see the modules section of this project). The main module performs the following tasks:

- Creates a Macie account for this AWS account
- Creates one or more Macie classification_jobs for this account if the classification_job var is not empty.
- Creates one or more Macie custom_data_identifiers for this account if the custom_data_identifier var is not empty.
- Creates one or more Macie findings_filters for this account if the findings_filter var is not empty.

### 0.0.1

**Released**: 2020.05.14
Expand Down
40 changes: 27 additions & 13 deletions README.md

Large diffs are not rendered by default.

212 changes: 162 additions & 50 deletions main.tf
Original file line number Diff line number Diff line change
@@ -1,69 +1,181 @@
provider "aws" {}

provider "aws" {
alias = "master"
# This file creates a standard Macie configuration in a single AWS account. These include a Macie classification_job, findings_filter, and custom_data_identifier. Macie configurations that require multiple AWS accounts are not included in this module, and the terraform code for those configurations has been implemented in seperate submodeles (see the modules section of this project).
#
# - Creates a Macie account for this AWS account
# - Creates one or more Macie classification_jobs for this account if the classification_job var is not empty.
# - Creates one or more Macie custom_data_identifiers for this account if the custom_data_identifier var is not empty.
# - Creates one or more Macie findings_filters for this account if the findings_filter var is not empty.
#
# Prerequisites: None

# Creates a Macie account in this AWS account
resource "aws_macie2_account" "this" {
finding_publishing_frequency = var.finding_publishing_frequency
status = var.status
}

data "aws_caller_identity" "this" {
}
# Creates one or more Macie classification_jobs for this AWS account if the threclassification_job atintelset var is not empty.
resource "aws_macie2_classification_job" "this" {
for_each = { for job in var.classification_jobs : job.name => job }

data "aws_caller_identity" "master" {
provider = aws.master
}
dynamic "schedule_frequency" {
for_each = each.value.schedule_frequency != null ? [each.value.schedule_frequency] : []

# policy document for MacieHandshake role
data "aws_iam_policy_document" "this" {
count = var.create_macie_member ? 1 : 0
content {
daily_schedule = schedule_frequency.value.daily_schedule
weekly_schedule = schedule_frequency.value.weekly_schedule
monthly_schedule = schedule_frequency.value.monthly_schedule
}
}

statement {
actions = ["sts:AssumeRole"]
custom_data_identifier_ids = each.value.custom_data_identifier_ids
sampling_percentage = each.value.sampling_percentage
name = each.value.name
description = each.value.description
initial_run = each.value.initial_run
job_type = each.value.job_type
job_status = each.value.job_status
tags = each.value.tags

principals {
type = "Service"
identifiers = ["macie.amazonaws.com"]
}
s3_job_definition {

condition {
test = "StringEquals"
variable = "sts:ExternalId"
dynamic "bucket_definitions" {
for_each = each.value.s3_job_definition.bucket_definitions != null ? each.value.s3_job_definition.bucket_definitions : []

values = [
data.aws_caller_identity.master.account_id
]
content {
account_id = bucket_definitions.value.account_id
buckets = bucket_definitions.value.buckets
}
}
dynamic "scoping" {
for_each = each.value.s3_job_definition.scoping != null ? [each.value.s3_job_definition.scoping] : []

content {
dynamic "excludes" {
for_each = scoping.value.excludes != null ? [scoping.value.excludes] : []

content {
dynamic "and" {
for_each = excludes.value.and != null ? excludes.value.and : []

content {
dynamic "simple_scope_term" {
for_each = and.value.simple_scope_term != null ? [and.value.simple_scope_term] : []

content {
comparator = simple_scope_term.value.comparator
values = simple_scope_term.value.values
key = simple_scope_term.value.key
}
}
dynamic "tag_scope_term" {
for_each = and.value.tag_scope_term != null ? [and.value.tag_scope_term] : []

content {
comparator = tag_scope_term.value.comparator
key = tag_scope_term.value.key
target = tag_scope_term.value.target

dynamic "tag_values" {
for_each = tag_scope_term.value.tag_values != null ? [tag_scope_term.value.tag_values] : []

content {
value = lookup(tag_values.value, "value", null)
key = lookup(tag_values.value, "key", null)
}
}
}
}
}
}
}
}
dynamic "includes" {
for_each = scoping.value.includes != null ? [scoping.value.includes] : []

content {
dynamic "and" {
for_each = includes.value.and != null ? includes.value.and : []

content {
dynamic "simple_scope_term" {
for_each = and.value.simple_scope_term != null ? [and.value.simple_scope_term] : []

content {
comparator = simple_scope_term.value.comparator
values = simple_scope_term.value.values
key = simple_scope_term.value.key
}
}
dynamic "tag_scope_term" {
for_each = and.value.tag_scope_term != null ? [and.value.tag_scope_term] : []

content {
comparator = tag_scope_term.value.comparator
key = tag_scope_term.value.key
target = tag_scope_term.value.target

dynamic "tag_values" {
for_each = tag_scope_term.value.tag_values != null ? [tag_scope_term.value.tag_values] : []

content {
value = lookup(tag_values.value, "value", null)
key = lookup(tag_values.value, "key", null)
}
}
}
}
}
}
}
}
}
}
}
}

# create the MacieHandshake role
resource "aws_iam_role" "this" {
count = var.create_macie_member ? 1 : 0

name = "AWSMacieServiceCustomerHandshakeRole"
description = "Allows the master account to create service-linked roles in the member accounts"
assume_role_policy = data.aws_iam_policy_document.this[0].json
tags = var.tags
depends_on = [aws_macie2_account.this]
}

resource "aws_iam_role_policy_attachment" "this" {
count = var.create_macie_member ? 1 : 0
# Creates one or more Macie custom_data_identifiers for this AWS account if the custom_data_identifier var is not empty.
resource "aws_macie2_custom_data_identifier" "this" {
for_each = { for identifier in var.custom_data_identifiers : identifier.name => identifier }

role = aws_iam_role.this[0].name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonMacieHandshakeRole"
regex = each.value.regex
keywords = each.value.keywords
ignore_words = each.value.ignore_words
name = each.value.name
name_prefix = each.value.name_prefix
description = each.value.description
maximum_match_distance = each.value.maximum_match_distance
tags = each.value.tags

# Creation of the MacieHandshake role sometimes takes a few seconds to register
# before the parent account can assume the role, so we pause for a few seconds
provisioner "local-exec" {
command = "python -c 'import time; time.sleep(10)'"
}
depends_on = [aws_macie2_account.this]
}

resource "aws_macie_member_account_association" "this" {
count = var.create_macie_member ? 1 : 0
provider = aws.master

member_account_id = data.aws_caller_identity.this.account_id
# Creates one or more Macie findings_filters for this AWS account if the custom_data_identifier var is not empty.
resource "aws_macie2_findings_filter" "this" {
for_each = { for filter in var.findings_filters : filter.name => filter }

name = each.value.name
name_prefix = each.value.name_prefix
description = each.value.description
action = each.value.action
position = each.value.position
tags = each.value.tags
finding_criteria {
dynamic "criterion" {
for_each = each.value.criterion
content {
field = criterion.value.field
eq_exact_match = criterion.value.eq_exact_match
eq = criterion.value.eq
neq = criterion.value.neq
lt = criterion.value.lt
lte = criterion.value.lte
gt = criterion.value.gt
gte = criterion.value.gte
}
}
}

depends_on = [
aws_iam_role_policy_attachment.this
]
depends_on = [aws_macie2_account.this]
}
45 changes: 45 additions & 0 deletions modules/member/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# terraform-aws-tardigrade-macie/member

Terraform module for managing a Macie member_account.

## Testing

You can find example implementations of this module in the tests folder (create_macie_member). This module requires 2 different AWS accounts to test.

<!-- BEGIN TFDOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.15 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 3.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 3.0 |
| <a name="provider_aws.administrator"></a> [aws.administrator](#provider\_aws.administrator) | >= 3.0 |

## Resources

| Name | Type |
|------|------|

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_member"></a> [member](#input\_member) | Macie member | <pre>object({<br> email = string # (Required) Email address for member account.<br> invitation_message = string # (Optional) A custom message to include in the invitation. Amazon Macie adds this message to the standard content that it sends for an invitation.<br> invitation_disable_email_notification = bool # (Optional) Specifies whether to send an email notification to the root user of each account that the invitation will be sent to. This notification is in addition to an alert that the root user receives in AWS Personal Health Dashboard. To send an email notification to the root user of each account, set this value to true.<br> tags = map(string) # (Optional) A map of key-value pairs that specifies the tags to associate with the account in Amazon Macie.<br> })</pre> | n/a | yes |
| <a name="input_finding_publishing_frequency"></a> [finding\_publishing\_frequency](#input\_finding\_publishing\_frequency) | (Optional) Specifies how often to publish updates to policy findings for the account. This includes publishing updates to AWS Security Hub and Amazon EventBridge (formerly called Amazon CloudWatch Events). Valid values are FIFTEEN\_MINUTES, ONE\_HOUR or SIX\_HOURS. | `string` | `"SIX_HOURS"` | no |
| <a name="input_status"></a> [status](#input\_status) | (Optional) Specifies the status for the account. To enable Amazon Macie and start all Macie activities for the account, set this value to ENABLED. Valid values are ENABLED or PAUSED. | `string` | `"ENABLED"` | no |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_account"></a> [account](#output\_account) | Macie account |
| <a name="output_invite_accepter"></a> [invite\_accepter](#output\_invite\_accepter) | Macie aws\_macie\_invite\_accepter |
| <a name="output_member"></a> [member](#output\_member) | Macie member configuration |

<!-- END TFDOCS -->
30 changes: 30 additions & 0 deletions modules/member/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Invites a member account to join an administrator account Macie organization.
# - Creates a Macie account in the member AWS account
# - Creates a macie member resource in the administrator account which imnvites the member account to join the administrator account Macie organization.
# - Creates a Macie invite accepter in the member account to accept the invite from the administrator account
#
# Prerequisites: The administrator AWS account's Macie account must already be enabled

# Creates a Macie account in the member (this) AWS account
resource "aws_macie2_account" "this" {
finding_publishing_frequency = var.finding_publishing_frequency
status = var.status
}

# Create Macie member in the administrator account
resource "aws_macie2_member" "this" {
provider = aws.administrator

account_id = aws_macie2_account.this.id
email = var.member.email
invite = true
invitation_message = var.member.invitation_message
invitation_disable_email_notification = var.member.invitation_disable_email_notification
status = var.status
tags = var.member.tags
}

# Create macie invite accepter in the member account
resource "aws_macie2_invitation_accepter" "this" {
administrator_account_id = aws_macie2_member.this.administrator_account_id
}
14 changes: 14 additions & 0 deletions modules/member/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
output "account" {
description = "Macie account"
value = aws_macie2_account.this
}

output "member" {
description = "Macie member configuration"
value = aws_macie2_member.this
}

output "invite_accepter" {
description = "Macie aws_macie_invite_accepter"
value = aws_macie2_invitation_accepter.this
}
Loading

0 comments on commit a0864f3

Please sign in to comment.