Skip to content

Commit

Permalink
Merge pull request #1 from Coalfire-CF/module-import
Browse files Browse the repository at this point in the history
Initial commit
  • Loading branch information
douglas-f authored Aug 10, 2023
2 parents 08e2760 + 1a60f73 commit 25fa243
Show file tree
Hide file tree
Showing 11 changed files with 483 additions and 22 deletions.
28 changes: 7 additions & 21 deletions .github/workflows/org-terraform-docs.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,10 @@
name: 'Terraform-Docs'

name: Org Terraform Docs
on:
pull_request:
workflow_call:

env:
TF_LOG: INFO
pull_request:
workflow_call:

jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.ref }}

- name: Render terraform docs inside the README.md and push changes back to PR branch
uses: terraform-docs/[email protected]
with:
working-dir: .
output-file: README.md
output-method: inject
git-push: "true"
terraform-docs:
uses: Coalfire-CF/Actions/.github/workflows/org-terraform-docs.yml@main
with:
recursive: true
54 changes: 53 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,53 @@
# ACE-GCP-Log-Export
# Google Cloud Log Export Terraform Module

This module allows you to create log exports at the project, folder, organization, or billing account level.
<!-- BEGIN_TF_DOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.5.0 |
| <a name="requirement_google"></a> [google](#requirement\_google) | >= 4.70, < 5.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_google"></a> [google](#provider\_google) | >= 4.70, < 5.0 |

## Modules

No modules.

## Resources

| Name | Type |
|------|------|
| [google_logging_billing_account_sink.sink](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/logging_billing_account_sink) | resource |
| [google_logging_folder_sink.sink](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/logging_folder_sink) | resource |
| [google_logging_organization_sink.sink](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/logging_organization_sink) | resource |
| [google_logging_project_sink.sink](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/logging_project_sink) | resource |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_destination_uri"></a> [destination\_uri](#input\_destination\_uri) | The self\_link URI of the destination resource (This is available as an output coming from one of the destination submodules) | `string` | n/a | yes |
| <a name="input_exclusions"></a> [exclusions](#input\_exclusions) | (Optional) A list of sink exclusion filters. | <pre>list(object({<br> name = string,<br> description = string,<br> filter = string,<br> disabled = bool<br> }))</pre> | `[]` | no |
| <a name="input_filter"></a> [filter](#input\_filter) | The filter to apply when exporting logs. Only log entries that match the filter are exported. Default is '' which exports all logs. | `string` | `""` | no |
| <a name="input_include_children"></a> [include\_children](#input\_include\_children) | Only valid if 'organization' or 'folder' is chosen as var.parent\_resource.type. Determines whether or not to include children organizations/folders in the sink export. If true, logs associated with child projects are also exported; otherwise only logs relating to the provided organization/folder are included. | `bool` | `false` | no |
| <a name="input_log_sink_name"></a> [log\_sink\_name](#input\_log\_sink\_name) | The name of the log sink to be created. | `string` | n/a | yes |
| <a name="input_parent_resource_id"></a> [parent\_resource\_id](#input\_parent\_resource\_id) | The ID of the GCP resource in which you create the log sink. If var.parent\_resource\_type is set to 'project', then this is the Project ID (and etc). | `string` | n/a | yes |
| <a name="input_parent_resource_type"></a> [parent\_resource\_type](#input\_parent\_resource\_type) | The GCP resource in which you create the log sink. The value must not be computed, and must be one of the following: 'project', 'folder', 'billing\_account', or 'organization'. | `string` | `"project"` | no |
| <a name="input_unique_writer_identity"></a> [unique\_writer\_identity](#input\_unique\_writer\_identity) | Whether or not to create a unique identity associated with this sink. If false (the default), then the writer\_identity used is serviceAccount:cloud-logs@system.gserviceaccount.com. If true, then a unique service account is created and used for the logging sink. | `bool` | `false` | no |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_filter"></a> [filter](#output\_filter) | The filter to be applied when exporting logs. |
| <a name="output_log_sink_resource_id"></a> [log\_sink\_resource\_id](#output\_log\_sink\_resource\_id) | The resource ID of the log sink that was created. |
| <a name="output_log_sink_resource_name"></a> [log\_sink\_resource\_name](#output\_log\_sink\_resource\_name) | The resource name of the log sink that was created. |
| <a name="output_parent_resource_id"></a> [parent\_resource\_id](#output\_parent\_resource\_id) | The ID of the GCP resource in which you create the log sink. |
| <a name="output_writer_identity"></a> [writer\_identity](#output\_writer\_identity) | The service account that logging uses to write log entries to the destination. |
<!-- END_TF_DOCS -->
95 changes: 95 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
locals {
is_project_level = var.parent_resource_type == "project"
is_folder_level = var.parent_resource_type == "folder"
is_org_level = var.parent_resource_type == "organization"
is_billing_level = var.parent_resource_type == "billing_account"

# Locals for outputs to ensure the value is available after the resource is created
log_sink_writer_identity = local.is_project_level ? element(concat(google_logging_project_sink.sink.*.writer_identity, [""]), 0) : local.is_folder_level ? element(concat(google_logging_folder_sink.sink.*.writer_identity, [""]), 0) : local.is_org_level ? element(concat(google_logging_organization_sink.sink.*.writer_identity, [""]), 0) : local.is_billing_level ? element(concat(google_logging_billing_account_sink.sink.*.writer_identity, [""]), 0) : ""
log_sink_resource_id = local.is_project_level ? element(concat(google_logging_project_sink.sink.*.id, [""]), 0) : local.is_folder_level ? element(concat(google_logging_folder_sink.sink.*.id, [""]), 0) : local.is_org_level ? element(concat(google_logging_organization_sink.sink.*.id, [""]), 0) : local.is_billing_level ? element(concat(google_logging_billing_account_sink.sink.*.id, [""]), 0) : ""
log_sink_resource_name = local.is_project_level ? element(concat(google_logging_project_sink.sink.*.name, [""]), 0) : local.is_folder_level ? element(concat(google_logging_folder_sink.sink.*.name, [""]), 0) : local.is_org_level ? element(concat(google_logging_organization_sink.sink.*.name, [""]), 0) : local.is_billing_level ? element(concat(google_logging_billing_account_sink.sink.*.name, [""]), 0) : ""
log_sink_parent_id = local.is_project_level ? element(concat(google_logging_project_sink.sink.*.project, [""]), 0) : local.is_folder_level ? element(concat(google_logging_folder_sink.sink.*.folder, [""]), 0) : local.is_org_level ? element(concat(google_logging_organization_sink.sink.*.org_id, [""]), 0) : local.is_billing_level ? element(concat(google_logging_billing_account_sink.sink.*.billing_account, [""]), 0) : ""
}

# Org-level
resource "google_logging_organization_sink" "sink" {
count = local.is_org_level ? 1 : 0
org_id = var.parent_resource_id

name = var.log_sink_name
filter = var.filter
include_children = var.include_children
destination = var.destination_uri

dynamic "exclusions" {
for_each = var.exclusions
content {
name = exclusions.value.name
description = exclusions.value.description
filter = exclusions.value.filter
disabled = exclusions.value.disabled
}
}
}

# Folder-level
resource "google_logging_folder_sink" "sink" {
count = local.is_folder_level ? 1 : 0
folder = var.parent_resource_id

name = var.log_sink_name
filter = var.filter
include_children = var.include_children
destination = var.destination_uri

dynamic "exclusions" {
for_each = var.exclusions
content {
name = exclusions.value.name
description = exclusions.value.description
filter = exclusions.value.filter
disabled = exclusions.value.disabled
}
}
}

# Project-level
resource "google_logging_project_sink" "sink" {
count = local.is_project_level ? 1 : 0
project = var.parent_resource_id

name = var.log_sink_name
filter = var.filter
destination = var.destination_uri
unique_writer_identity = var.unique_writer_identity

dynamic "exclusions" {
for_each = var.exclusions
content {
name = exclusions.value.name
description = exclusions.value.description
filter = exclusions.value.filter
disabled = exclusions.value.disabled
}
}
}

# Billing Account-level
resource "google_logging_billing_account_sink" "sink" {
count = local.is_billing_level ? 1 : 0
billing_account = var.parent_resource_id

name = var.log_sink_name
filter = var.filter
destination = var.destination_uri

dynamic "exclusions" {
for_each = var.exclusions
content {
name = exclusions.value.name
description = exclusions.value.description
filter = exclusions.value.filter
disabled = exclusions.value.disabled
}
}
}
61 changes: 61 additions & 0 deletions modules/pubsub/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Google Cloud Log Export Pub/Sub Destination Terraform Module

This module allows you to configure a Pub/Sub topic destination that can be used by the log export module.
<!-- BEGIN_TF_DOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.5.0 |
| <a name="requirement_google"></a> [google](#requirement\_google) | >= 4.70, < 5.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_google"></a> [google](#provider\_google) | >= 4.70, < 5.0 |

## Modules

No modules.

## Resources

| Name | Type |
|------|------|
| [google_pubsub_subscription.pubsub_push_subscription](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/pubsub_subscription) | resource |
| [google_pubsub_subscription.pubsub_subscription](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/pubsub_subscription) | resource |
| [google_pubsub_subscription_iam_member.pubsub_subscriber_role](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/pubsub_subscription_iam_member) | resource |
| [google_pubsub_topic.topic](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/pubsub_topic) | resource |
| [google_pubsub_topic_iam_member.pubsub_sink_member](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/pubsub_topic_iam_member) | resource |
| [google_pubsub_topic_iam_member.pubsub_viewer_role](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/pubsub_topic_iam_member) | resource |
| [google_service_account.pubsub_subscriber](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account) | resource |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_create_push_subscriber"></a> [create\_push\_subscriber](#input\_create\_push\_subscriber) | Whether to add a push configuration to the subcription. If 'true', a push subscription is created for push\_endpoint | `bool` | `false` | no |
| <a name="input_create_subscriber"></a> [create\_subscriber](#input\_create\_subscriber) | Whether to create a subscription to the topic that was created and used for log entries matching the filter. If 'true', a pull subscription is created along with a service account that is granted roles/pubsub.subscriber and roles/pubsub.viewer to the topic. | `bool` | `false` | no |
| <a name="input_kms_key_name"></a> [kms\_key\_name](#input\_kms\_key\_name) | ID of a Cloud KMS CryptoKey to be used to protect access to messages published on this topic. Your project's PubSub service account requires access to this encryption key. | `string` | `null` | no |
| <a name="input_log_sink_writer_identity"></a> [log\_sink\_writer\_identity](#input\_log\_sink\_writer\_identity) | The service account that logging uses to write log entries to the destination. (This is available as an output coming from the root module). | `string` | n/a | yes |
| <a name="input_project_id"></a> [project\_id](#input\_project\_id) | The ID of the project in which the pubsub topic will be created. | `string` | n/a | yes |
| <a name="input_push_endpoint"></a> [push\_endpoint](#input\_push\_endpoint) | The URL locating the endpoint to which messages should be pushed. | `string` | `""` | no |
| <a name="input_subscriber_id"></a> [subscriber\_id](#input\_subscriber\_id) | The ID to give the pubsub pull subscriber service account (optional). | `string` | `""` | no |
| <a name="input_subscription_labels"></a> [subscription\_labels](#input\_subscription\_labels) | A set of key/value label pairs to assign to the pubsub subscription or pubsub push subscription. | `map(string)` | `{}` | no |
| <a name="input_topic_labels"></a> [topic\_labels](#input\_topic\_labels) | A set of key/value label pairs to assign to the pubsub topic. | `map(string)` | `{}` | no |
| <a name="input_topic_name"></a> [topic\_name](#input\_topic\_name) | The name of the pubsub topic to be created and used for log entries matching the filter. | `string` | n/a | yes |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_console_link"></a> [console\_link](#output\_console\_link) | The console link to the destination storage bucket |
| <a name="output_destination_uri"></a> [destination\_uri](#output\_destination\_uri) | The destination URI for the topic. |
| <a name="output_project"></a> [project](#output\_project) | The project in which the topic was created. |
| <a name="output_pubsub_push_subscription"></a> [pubsub\_push\_subscription](#output\_pubsub\_push\_subscription) | Pub/Sub push subscription id (if any) |
| <a name="output_pubsub_subscriber"></a> [pubsub\_subscriber](#output\_pubsub\_subscriber) | Pub/Sub subscriber email (if any) |
| <a name="output_pubsub_subscription"></a> [pubsub\_subscription](#output\_pubsub\_subscription) | Pub/Sub subscription id (if any) |
| <a name="output_resource_id"></a> [resource\_id](#output\_resource\_id) | The resource id for the destination topic |
| <a name="output_resource_name"></a> [resource\_name](#output\_resource\_name) | The resource name for the destination topic |
<!-- END_TF_DOCS -->
81 changes: 81 additions & 0 deletions modules/pubsub/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
locals {
destination_uri = "pubsub.googleapis.com/projects/${var.project_id}/topics/${local.topic_name}"
topic_name = element(concat(google_pubsub_topic.topic.*.name, [""]), 0)
pubsub_subscriber = element(
concat(google_service_account.pubsub_subscriber.*.email, [""]),
0,
)
pubsub_subscription = element(
concat(google_pubsub_subscription.pubsub_subscription.*.id, [""]),
0,
)
pubsub_push_subscription = element(
concat(google_pubsub_subscription.pubsub_push_subscription.*.id, [""]),
0,
)
subscriber_id = var.subscriber_id == "" ? "${local.topic_name}-subscriber" : var.subscriber_id
}

resource "google_pubsub_topic" "topic" {
project = var.project_id

name = var.topic_name
labels = var.topic_labels
kms_key_name = var.kms_key_name
}

resource "google_pubsub_topic_iam_member" "pubsub_sink_member" {
project = var.project_id

topic = local.topic_name
role = "roles/pubsub.publisher"
member = var.log_sink_writer_identity
}

resource "google_service_account" "pubsub_subscriber" {
count = var.create_subscriber ? 1 : 0
project = var.project_id

account_id = local.subscriber_id
display_name = "${local.topic_name} Topic Subscriber"
}

resource "google_pubsub_subscription_iam_member" "pubsub_subscriber_role" {
count = var.create_subscriber ? 1 : 0
project = var.project_id

role = "roles/pubsub.subscriber"
subscription = local.pubsub_subscription
member = "serviceAccount:${google_service_account.pubsub_subscriber[0].email}"
}

resource "google_pubsub_topic_iam_member" "pubsub_viewer_role" {
count = var.create_subscriber ? 1 : 0
project = var.project_id

role = "roles/pubsub.viewer"
topic = local.topic_name
member = "serviceAccount:${google_service_account.pubsub_subscriber[0].email}"
}

resource "google_pubsub_subscription" "pubsub_subscription" {
count = var.create_subscriber ? 1 : 0
project = var.project_id

name = "${local.topic_name}-subscription"
topic = local.topic_name
labels = var.subscription_labels
}

resource "google_pubsub_subscription" "pubsub_push_subscription" {
count = var.create_push_subscriber ? 1 : 0
project = var.project_id

name = "${local.topic_name}-push-subscription"
topic = local.topic_name
labels = var.subscription_labels

push_config {
push_endpoint = var.push_endpoint
}
}
39 changes: 39 additions & 0 deletions modules/pubsub/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
output "console_link" {
description = "The console link to the destination storage bucket"
value = "https://console.cloud.google.com/cloudpubsub/topics/${local.topic_name}?project=${var.project_id}"
}

output "project" {
description = "The project in which the topic was created."
value = google_pubsub_topic.topic.project
}

output "resource_name" {
description = "The resource name for the destination topic"
value = local.topic_name
}

output "resource_id" {
description = "The resource id for the destination topic"
value = google_pubsub_topic.topic.id
}

output "destination_uri" {
description = "The destination URI for the topic."
value = local.destination_uri
}

output "pubsub_subscriber" {
description = "Pub/Sub subscriber email (if any)"
value = local.pubsub_subscriber
}

output "pubsub_subscription" {
description = "Pub/Sub subscription id (if any)"
value = local.pubsub_subscription
}

output "pubsub_push_subscription" {
description = "Pub/Sub push subscription id (if any)"
value = local.pubsub_push_subscription
}
Loading

0 comments on commit 25fa243

Please sign in to comment.