Skip to content

Commit 97ed72b

Browse files
authored
feat: add cloudtrail storage module (#1)
* feat: very opinionated storage for cloudtrail * chore: more work * ci: install checkov * ci: disable checkov until we can get it installed in ci * ci: fix auto lockfile cleanups
1 parent 15206be commit 97ed72b

File tree

8 files changed

+232
-27
lines changed

8 files changed

+232
-27
lines changed

.github/workflows/pre-commit.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ jobs:
3434
- name: Checkout
3535
uses: actions/checkout@v4
3636

37+
- uses: actions/setup-python@v5
38+
with:
39+
python-version: '3.12'
40+
41+
- name: Install checkov
42+
run: pip install checkov
43+
3744
- name: Terraform min/max versions
3845
id: minMax
3946
uses: clowdhaus/terraform-min-max@v1.2.7

.pre-commit-config.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,13 @@ repos:
4242
- id: terraform_fmt
4343
- id: terraform_docs
4444
- id: terraform_validate
45+
args:
46+
- --hook-config=--retry-once-with-cleanup=true
47+
# - id: terraform_checkov
48+
- id: terraform_providers_lock
49+
args:
50+
- --hook-config=--mode=only-check-is-current-lockfile-cross-platform
51+
- --args=-platform=darwin_amd64
52+
- --args=-platform=darwin_arm64
53+
- --args=-platform=linux_arm64
54+
- --args=-platform=linux_arm64

.terraform.lock.hcl

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,32 @@
1-
# AWS {{THING}} Terraform module
1+
# AWS S3 Bucket for Multi-Account Cloudtrail Storage Terraform module
22

3-
Terraform module which creates AWS {{THING}} resources.
3+
Terraform module which creates an AWS S3 Bucket for Multi-Account Cloudtrail logs.
44

5-
## Usage
5+
This is an opinionated tool for setting up a central bucket in an audit account to house multiple cloudtrail logs streams. Not recommended for trails with data events due to cost.
66

7-
See [`examples`](examples) directory for working examples to reference:
7+
Features:
8+
- AWS S3 default encryption for data at rest
9+
- 365 day object lock in GOVERNANCE mode to prevent source file issues
10+
- 366 day auto expiration
811

9-
```hcl
10-
module "<THING>" {
11-
source = "platformod/<THING>"
12+
## Usage
1213

13-
tags = {
14-
Terraform = "true"
15-
Environment = "dev"
16-
}
14+
```hcl
15+
module "cloudtrail_s3" {
16+
source = "platformod/cloudtrail-s3"
17+
version = 0.CHANGE_ME
18+
19+
# will get '-cloudtrail' appended
20+
name = "my-org-all-accounts"
21+
22+
# Needs a list of maps with the accounts and trail arn that will write to this bucket.
23+
account_trails = [
24+
{account = 111111111111, arn = "arn:aws:cloudtrail:us-east-99:111111111111:trail/trail-name"},
25+
{account = 222222222222, arn = "arn:aws:cloudtrail:us-westish-42:222222222222:trail/trailier-name"},
26+
]
1727
}
18-
```
1928
20-
## Examples
21-
22-
Examples codified under the [`examples`](examples) are intended to give users references for how to use the module(s) as well as testing/validating changes to the source code of the module. If contributing to the project, please be sure to make any appropriate updates to the relevant examples to allow maintainers to test your changes and to keep the examples up to date for users. Thank you!
29+
```
2330

2431
- [Complete](complete)
2532

@@ -33,32 +40,47 @@ Examples codified under the [`examples`](examples) are intended to give users re
3340

3441
## Providers
3542

36-
No providers.
43+
| Name | Version |
44+
|------|---------|
45+
| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.34.0 |
3746

3847
## Modules
3948

40-
No modules.
49+
| Name | Source | Version |
50+
|------|--------|---------|
51+
| <a name="module_bucket"></a> [bucket](#module\_bucket) | terraform-aws-modules/s3-bucket/aws | 4.1.0 |
4152

4253
## Resources
4354

44-
No resources.
55+
| Name | Type |
56+
|------|------|
57+
| [aws_iam_policy_document.bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
58+
| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source |
4559

4660
## Inputs
4761

48-
No inputs.
62+
| Name | Description | Type | Default | Required |
63+
|------|-------------|------|---------|:--------:|
64+
| <a name="input_account_trails"></a> [account\_trails](#input\_account\_trails) | Mapping of AWS account id's to trail arns to allow write access for | <pre>list(<br> object(<br> {<br> account = number<br> arn = string<br> }<br> )<br> )</pre> | n/a | yes |
65+
| <a name="input_name"></a> [name](#input\_name) | A name prefix for the bucket, will have '-cloudtrail' appended | `string` | n/a | yes |
4966

5067
## Outputs
5168

52-
No outputs.
69+
| Name | Description |
70+
|------|-------------|
71+
| <a name="output_s3_bucket_arn"></a> [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | The AWS ARN of the bucket |
72+
| <a name="output_s3_bucket_id"></a> [s3\_bucket\_id](#output\_s3\_bucket\_id) | The name of the bucket |
5373
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
5474

75+
## Tests
76+
77+
The tests in this repo will create and destroy real resources at AWS and incur cost. Please be careful when running them.
5578

5679
## Thanks
5780

58-
Heavily inspired from the following template repos
59-
* https://github.com/clowdhaus/terraform-aws-module-template
60-
* https://github.com/trussworks/terraform-module-template
61-
* https://github.com/thesis/terraform-module-template-repo
81+
Heavily inspired from the following repos
82+
* https://github.com/cloudposse/terraform-aws-s3-log-storage
83+
* https://github.com/terraform-aws-modules/terraform-aws-s3-bucket
6284

6385
## License
6486

main.tf

Lines changed: 116 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,116 @@
1-
# Implementation is left as an excercise for the reader ...
2-
locals {}
1+
module "bucket" {
2+
#checkov:skip=CKV_TF_1:We want to use the registry, not git, for modules
3+
source = "terraform-aws-modules/s3-bucket/aws"
4+
version = "4.1.0"
5+
6+
bucket = local.bucket_name
7+
8+
control_object_ownership = true
9+
object_ownership = "BucketOwnerPreferred"
10+
11+
acl = "private"
12+
block_public_acls = true
13+
block_public_policy = true
14+
15+
policy = data.aws_iam_policy_document.bucket.json
16+
attach_policy = true
17+
attach_deny_insecure_transport_policy = true
18+
19+
server_side_encryption_configuration = {
20+
rule = {
21+
apply_server_side_encryption_by_default = {
22+
sse_algorithm = "AES256"
23+
}
24+
}
25+
}
26+
27+
object_lock_enabled = true
28+
object_lock_configuration = {
29+
rule = {
30+
default_retention = {
31+
mode = "GOVERNANCE"
32+
days = 365
33+
}
34+
}
35+
}
36+
37+
lifecycle_rule = [
38+
{
39+
id = "expire_old"
40+
enabled = true
41+
abort_incomplete_multipart_upload_days = 7
42+
expiration = {
43+
days = 366
44+
expired_object_delete_marker = true
45+
}
46+
}
47+
]
48+
49+
tags = {
50+
Purpose = "cloudtrail"
51+
Name = local.bucket_name
52+
}
53+
54+
}
55+
56+
# https://docs.aws.amazon.com/awscloudtrail/latest/userguide/create-s3-bucket-policy-for-cloudtrail.html
57+
# https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-set-bucket-policy-for-multiple-accounts.html
58+
data "aws_iam_policy_document" "bucket" {
59+
statement {
60+
sid = "AWSCloudTrailAclCheck"
61+
62+
principals {
63+
type = "Service"
64+
identifiers = ["cloudtrail.amazonaws.com"]
65+
}
66+
67+
actions = [
68+
"s3:GetBucketAcl",
69+
]
70+
71+
resources = [
72+
module.bucket.s3_bucket_arn,
73+
]
74+
75+
condition {
76+
test = "StringEquals"
77+
variable = "aws:SourceArn"
78+
values = [for o in var.account_trails : o.arn]
79+
}
80+
}
81+
82+
statement {
83+
sid = "AWSCloudTrailWrite"
84+
85+
principals {
86+
type = "Service"
87+
identifiers = ["cloudtrail.amazonaws.com"]
88+
}
89+
90+
actions = [
91+
"s3:PutObject",
92+
]
93+
94+
resources = [for o in var.account_trails : "${local.bucket_arn}/AWSLogs/${o.account}"]
95+
96+
condition {
97+
test = "StringEquals"
98+
variable = "aws:SourceArn"
99+
values = [for o in var.account_trails : o.arn]
100+
}
101+
102+
condition {
103+
test = "StringEquals"
104+
variable = "s3:x-amz-acl"
105+
values = ["bucket-owner-full-control"]
106+
}
107+
108+
}
109+
}
110+
111+
data "aws_partition" "current" {}
112+
113+
locals {
114+
bucket_name = "${var.name}-cloudtrail"
115+
bucket_arn = "arn:${data.aws_partition.current.partition}:s3:::${local.bucket_name}"
116+
}

outputs.tf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
output "s3_bucket_id" {
2+
description = "The name of the bucket"
3+
value = module.bucket.s3_bucket_id
4+
}
5+
6+
output "s3_bucket_arn" {
7+
description = "The AWS ARN of the bucket"
8+
value = module.bucket.s3_bucket_arn
9+
}

tests/plan.tftest.hcl

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
provider "aws" {
2+
region = "us-east-2"
3+
default_tags {
4+
tags = {
5+
Environment = "Test"
6+
Repo = "platformod/terraform-aws-cloudtrail-s3"
7+
CI = true
8+
}
9+
}
10+
}
11+
12+
variables {
13+
name = "test-8388737"
14+
account_trails = [
15+
{account = 111111111111, arn = "arn:aws:cloudtrail:us-fake-1:111111111111:trail/trail-name"},
16+
{account = 222222222222, arn = "arn:aws:cloudtrail:us-fake-1:222222222222:trail/trail-name"},
17+
]
18+
}
19+
20+
run "apply" {
21+
command = apply
22+
23+
assert {
24+
condition = module.bucket.s3_bucket_id == "test-8388737-cloudtrail"
25+
error_message = "S3 bucket name did not match expected"
26+
}
27+
}

variables.tf

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
variable "account_trails" {
2+
description = "Mapping of AWS account id's to trail arns to allow write access for "
3+
type = list(
4+
object(
5+
{
6+
account = number
7+
arn = string
8+
}
9+
)
10+
)
11+
}
12+
13+
variable "name" {
14+
description = "A name prefix for the bucket, will have '-cloudtrail' appended"
15+
type = string
16+
}

0 commit comments

Comments
 (0)