Skip to content

Commit d87b007

Browse files
authored
Enable logging with private s3 bucket (#210)
1 parent 90a9ec4 commit d87b007

File tree

6 files changed

+238
-0
lines changed

6 files changed

+238
-0
lines changed

.github/workflows/ci.yml

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ jobs:
3737
aws-aurora,
3838
aws-aurora-mysql,
3939
aws-aurora-postgres,
40+
aws-cloudfront-logs-bucket,
4041
aws-cloudwatch-log-group,
4142
aws-default-vpc-security,
4243
aws-ecs-job,

aws-cloudfront-logs-bucket/README.md

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# aws-cloudfront-logs-bucket
2+
3+
This module uses the `aws-s3-private-bucket` module as its source and enables logging for Cloudfront to the specified S3 bucket. We include the grant to `aws-logs-delivery` whose canonical id is `c4c1ede66af53448b93c283ce9448c4ba468c9432aa01d700d3878632f77d2d0`, documentation for this can be found [here](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html#AccessLogsBucketAndFileOwnership). The suggestion is found here:
4+
5+
```
6+
Restoring the ACL for the bucket
7+
If you remove permissions for the awslogsdelivery account, CloudFront won't be able to save logs to the S3 bucket. To enable CloudFront to start saving logs for your distribution again, restore the ACL permission by doing one of the following:
8+
9+
...
10+
11+
Add the ACL permission for awslogsdelivery manually by navigating to the S3 bucket in the Amazon S3 console and adding permission. To add the ACL for awslogsdelivery, you must provide the canonical ID for the account, which is the following:
12+
13+
c4c1ede66af53448b93c283ce9448c4ba468c9432aa01d700d3878632f77d2d0
14+
```
15+
16+
## Example
17+
18+
```hcl
19+
module "s3-bucket" {
20+
source = "github.com/chanzuckerberg/cztack/aws-cloudfront-logs-bucket?ref=v0.33.1"
21+
bucket_name = "..."
22+
env = var.env
23+
owner = var.owner
24+
project = var.project
25+
service = var.component
26+
}
27+
```
28+
29+
<!-- START -->
30+
## Requirements
31+
32+
No requirements.
33+
34+
## Providers
35+
36+
| Name | Version |
37+
|------|---------|
38+
| aws | n/a |
39+
40+
## Inputs
41+
42+
| Name | Description | Type | Default | Required |
43+
|------|-------------|------|---------|:--------:|
44+
| abort\_incomplete\_multipart\_upload\_days | Number of days after which an incomplete multipart upload is canceled. | `number` | `14` | no |
45+
| bucket\_name | n/a | `string` | n/a | yes |
46+
| bucket\_policy | n/a | `string` | `""` | no |
47+
| enable\_versioning | Keep old versions of overwritten S3 objects. | `bool` | `true` | no |
48+
| env | n/a | `string` | n/a | yes |
49+
| lifecycle\_rules | List of maps containing configuration of object lifecycle management. | `any` | <pre>[<br> {<br> "enabled": true,<br> "expiration": {<br> "expired_object_delete_marker": true<br> },<br> "noncurrent_version_expiration": {<br> "days": 365<br> },<br> "noncurrent_version_transition": {<br> "days": 30,<br> "storage_class": "STANDARD_IA"<br> }<br> }<br>]</pre> | no |
50+
| owner | n/a | `string` | n/a | yes |
51+
| project | n/a | `string` | n/a | yes |
52+
| public\_access\_block | n/a | `bool` | `true` | no |
53+
| service | n/a | `string` | n/a | yes |
54+
55+
## Outputs
56+
57+
| Name | Description |
58+
|------|-------------|
59+
| arn | n/a |
60+
| domain\_name | n/a |
61+
| id | n/a |
62+
| name | HACK(el): we do this to hint TF dependency graph since modules can't depend\_on |
63+
64+
<!-- END -->

aws-cloudfront-logs-bucket/main.tf

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
locals {
2+
# Define the grant ACL for the Cloudfront logging S3 bucket,
3+
# In order for the awslogsdelivery account to write log files to the bucket,
4+
# we need to grant the AWS log delivery group the FULL_CONTROL access to the logging bucket
5+
grants = [
6+
{
7+
canonical_user_id : data.aws_canonical_user_id.current_user.id
8+
permissions : ["FULL_CONTROL"]
9+
10+
},
11+
{
12+
canonical_user_id : "c4c1ede66af53448b93c283ce9448c4ba468c9432aa01d700d3878632f77d2d0" # AWS log delivery group's canonical user id
13+
permissions : ["FULL_CONTROL"]
14+
15+
}
16+
]
17+
}
18+
19+
data "aws_canonical_user_id" "current_user" {}
20+
21+
module "aws-cloudfront-logs-bucket" {
22+
source = "../aws-s3-private-bucket"
23+
grants = local.grants
24+
env = var.env
25+
owner = var.owner
26+
project = var.project
27+
service = var.service
28+
bucket_name = var.bucket_name
29+
bucket_policy = var.bucket_policy
30+
enable_versioning = var.enable_versioning
31+
abort_incomplete_multipart_upload_days = var.abort_incomplete_multipart_upload_days
32+
public_access_block = var.public_access_block
33+
lifecycle_rules = var.lifecycle_rules
34+
}
+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/aws/aws-sdk-go/service/s3"
7+
"github.com/chanzuckerberg/cztack/testutil"
8+
"github.com/gruntwork-io/terratest/modules/aws"
9+
"github.com/gruntwork-io/terratest/modules/terraform"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
func TestPrivateBucketDefaults(t *testing.T) {
14+
15+
test := &testutil.Test{
16+
Options: func(t *testing.T) *terraform.Options {
17+
project := testutil.UniqueId()
18+
env := testutil.UniqueId()
19+
service := testutil.UniqueId()
20+
owner := testutil.UniqueId()
21+
22+
bucketName := testutil.UniqueId()
23+
24+
return testutil.Options(
25+
testutil.DefaultRegion,
26+
map[string]interface{}{
27+
"project": project,
28+
"env": env,
29+
"service": service,
30+
"owner": owner,
31+
32+
"bucket_name": bucketName,
33+
},
34+
)
35+
},
36+
37+
Validate: func(t *testing.T, options *terraform.Options) {
38+
r := require.New(t)
39+
region := options.EnvVars["AWS_DEFAULT_REGION"]
40+
bucket := options.Vars["bucket_name"].(string)
41+
42+
// get a client to query for other assertions
43+
s3Client := aws.NewS3Client(t, region)
44+
45+
acl, err := s3Client.GetBucketAcl(&s3.GetBucketAclInput{
46+
Bucket: &bucket,
47+
})
48+
49+
r.NoError(err)
50+
r.Len(acl.Grants, 2)
51+
52+
r.Equal("CanonicalUser", *acl.Grants[0].Grantee.Type)
53+
r.Equal("FULL_CONTROL", *acl.Grants[0].Permission)
54+
r.Equal("c4c1ede66af53448b93c283ce9448c4ba468c9432aa01d700d3878632f77d2d0", *acl.Grants[1].Grantee.ID)
55+
r.Equal("FULL_CONTROL", *acl.Grants[1].Permission)
56+
},
57+
}
58+
test.Run(t)
59+
}

aws-cloudfront-logs-bucket/outputs.tf

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// HACK(el): we do this to hint TF dependency graph since modules can't depend_on
2+
output "name" {
3+
value = module.aws-cloudfront-logs-bucket.name
4+
}
5+
6+
output "domain_name" {
7+
value = module.aws-cloudfront-logs-bucket.domain_name
8+
}
9+
10+
output "arn" {
11+
value = module.aws-cloudfront-logs-bucket.arn
12+
}
13+
14+
output "id" {
15+
value = module.aws-cloudfront-logs-bucket.id
16+
}
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
variable "bucket_name" {
2+
type = string
3+
}
4+
5+
variable "bucket_policy" {
6+
type = string
7+
default = ""
8+
}
9+
10+
variable "project" {
11+
type = string
12+
}
13+
14+
variable "env" {
15+
type = string
16+
}
17+
18+
variable "service" {
19+
type = string
20+
}
21+
22+
variable "owner" {
23+
type = string
24+
}
25+
26+
variable "enable_versioning" {
27+
type = bool
28+
description = "Keep old versions of overwritten S3 objects."
29+
default = true
30+
}
31+
32+
variable "abort_incomplete_multipart_upload_days" {
33+
type = number
34+
description = "Number of days after which an incomplete multipart upload is canceled."
35+
default = 14
36+
}
37+
38+
variable "lifecycle_rules" {
39+
description = "List of maps containing configuration of object lifecycle management."
40+
type = any
41+
default = [
42+
{
43+
enabled = true
44+
45+
expiration = {
46+
expired_object_delete_marker = true
47+
}
48+
49+
noncurrent_version_transition = {
50+
days = 30
51+
storage_class = "STANDARD_IA"
52+
}
53+
54+
noncurrent_version_expiration = {
55+
days = 365
56+
}
57+
}
58+
]
59+
}
60+
61+
variable public_access_block {
62+
type = bool
63+
default = true
64+
}

0 commit comments

Comments
 (0)