Skip to content

Commit

Permalink
Fixes terraform-google-modules#11: Cloud Function for automatic folde…
Browse files Browse the repository at this point in the history
…r inclusion

terraform-google-modules#11
terraform-google-modules#14

Added example with cloud function that automatically runs TF when
project is created/moved/deleted.
  • Loading branch information
nick4fake committed Sep 11, 2019
1 parent 875def2 commit 36626c8
Show file tree
Hide file tree
Showing 17 changed files with 413 additions and 14 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,8 @@ examples/simple_example_access_level/terraform.tfvars
terraform.tfvars

credentials.json

examples/automatic_folder.zip

node_modules
yarn.lock
6 changes: 0 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ version:
docker_run:
docker run --rm -it \
-e PROJECT_ID \
-e BUCKET_NAME \
-e SERVICE_ACCOUNT_JSON \
-e CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE=${CREDENTIALS_PATH} \
-e GOOGLE_APPLICATION_CREDENTIALS=${CREDENTIALS_PATH} \
Expand All @@ -101,7 +100,6 @@ docker_run:
docker_create:
docker run --rm -it \
-e PROJECT_ID \
-e BUCKET_NAME \
-e SERVICE_ACCOUNT_JSON \
-e CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE=${CREDENTIALS_PATH} \
-e GOOGLE_APPLICATION_CREDENTIALS=${CREDENTIALS_PATH} \
Expand All @@ -113,7 +111,6 @@ docker_create:
docker_converge:
docker run --rm -it \
-e PROJECT_ID \
-e BUCKET_NAME \
-e SERVICE_ACCOUNT_JSON \
-e CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE=${CREDENTIALS_PATH} \
-e GOOGLE_APPLICATION_CREDENTIALS=${CREDENTIALS_PATH} \
Expand All @@ -125,7 +122,6 @@ docker_converge:
docker_verify:
docker run --rm -it \
-e PROJECT_ID \
-e BUCKET_NAME \
-e SERVICE_ACCOUNT_JSON \
-e CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE=${CREDENTIALS_PATH} \
-e GOOGLE_APPLICATION_CREDENTIALS=${CREDENTIALS_PATH} \
Expand All @@ -137,7 +133,6 @@ docker_verify:
docker_destroy:
docker run --rm -it \
-e PROJECT_ID \
-e BUCKET_NAME \
-e SERVICE_ACCOUNT_JSON \
-e CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE=${CREDENTIALS_PATH} \
-e GOOGLE_APPLICATION_CREDENTIALS=${CREDENTIALS_PATH} \
Expand All @@ -149,7 +144,6 @@ docker_destroy:
test_integration_docker:
docker run --rm -it \
-e PROJECT_ID \
-e BUCKET_NAME \
-e SERVICE_ACCOUNT_JSON \
-e CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE=${CREDENTIALS_PATH} \
-e GOOGLE_APPLICATION_CREDENTIALS=${CREDENTIALS_PATH} \
Expand Down
4 changes: 4 additions & 0 deletions examples/automatic_folder/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/provider.tf
/.terraform
tfplan
local.tfvars
52 changes: 52 additions & 0 deletions examples/automatic_folder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Automatic folder securing Example

This example illustrates how to use the `vpc-service-controls` module to configure an org policy, an access level and a regular perimeter with projects inside a folder.

# Requirements

1. Make sure you've gone through the root [Requirement Section](../../README.md#requirements) on any project in your organization.
2. Updated `provider.tf.dist` with remote state configs. Copy `provider.tf.dist` to `provider.tf` changing variables for local running
3. Create `local.tfvars` file with required inputs, like this:
````hcl-terraform
project_id = "YOUR_PROJECT"
parent_id = "ORG_ID"
folder_id = "FOLDER_ID"
policy_name = "automatic_folder"
members = ["user:[email protected]"]
region = "us-east1"
restricted_services = ["storage.googleapis.com"]
````
4. Please note, that whole example folder is uploaded as Cloud Function root. Don't store credentials in it!
5. Add Cloud Function's SA to organization (Access Context Manager Admin), project IAM (Owner and Storage Object Admin) and watched folder (Logs Configuration Writer)
6. You might need to apply TF changes twice due to ACM race condition



<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|:----:|:-----:|:-----:|
| folder\_id | Folder ID to watch for projects. | string | n/a | yes |
| members | An allowed list of members \(users, service accounts\). The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user \(logged in/not logged in, etc.\). Formats: user:\{emailid\}, serviceAccount:\{emailid\} | list(string) | n/a | yes |
| parent\_id | The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent \(ID\). | string | n/a | yes |
| perimeter\_name | Name of perimeter. | string | `"regular_perimeter"` | no |
| policy\_name | The policy's name. | string | n/a | yes |
| project\_id | The ID of the project to which resources will be applied. | string | n/a | yes |
| region | The region in which resources will be applied. | string | n/a | yes |
| restricted\_services | List of services to restrict. | list(string) | n/a | yes |

## Outputs

| Name | Description |
|------|-------------|
| policy\_name | Name of the parent policy |
| protected\_project\_ids | Project ids of the projects INSIDE the regular service perimeter |

<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

To provision this example, run the following from within this directory:
- `terraform init` to get the plugins
- `terraform plan` to see the infrastructure plan
- `terraform apply` to apply the infrastructure build
- `terraform destroy` to destroy the built infrastructure
77 changes: 77 additions & 0 deletions examples/automatic_folder/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-

import os
import subprocess
import urllib.request
import os
from shutil import copytree, copyfile, ignore_patterns, rmtree

# Version of Terraform that we're using
TERRAFORM_VERSION = '0.12.8'

# Download URL for Terraform
TERRAFORM_DOWNLOAD_URL = (
'https://releases.hashicorp.com/terraform/%s/terraform_%s_linux_amd64.zip'
% (TERRAFORM_VERSION, TERRAFORM_VERSION))

# Paths where Terraform should be installed
TERRAFORM_DIR = os.path.join('/tmp', 'terraform_%s' % TERRAFORM_VERSION)
TERRAFORM_PATH = os.path.join(TERRAFORM_DIR, 'terraform')

PROJECT_DIR = os.path.join('/tmp', 'project')

def check_call(args, cwd=None, printOut=False):
"""Wrapper for subprocess that checks if a process runs correctly,
and if not, prints stdout and stderr.
"""
proc = subprocess.Popen(args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=cwd)
stdout, stderr = proc.communicate()
if proc.returncode != 0:
print(stdout)
print(stderr)
raise subprocess.CalledProcessError(
returncode=proc.returncode,
cmd=args)
if printOut:
print(stdout)
print(stderr)


def install_terraform():
"""Install Terraform."""
if os.path.exists(TERRAFORM_PATH):
return

urllib.request.urlretrieve(TERRAFORM_DOWNLOAD_URL, '/tmp/terraform.zip')

check_call(['unzip', '-o', '/tmp/terraform.zip', '-d', TERRAFORM_DIR], '/tmp')

check_call([TERRAFORM_PATH, '--version'])


def handler(event, context):
print(event)

if os.path.exists(PROJECT_DIR):
rmtree(PROJECT_DIR)

copytree('.', PROJECT_DIR, ignore=ignore_patterns('.terraform', 'provider.tf', 'credentials.json'))
copyfile('provider.tf.dist', os.path.join(PROJECT_DIR, 'provider.tf'))

install_terraform()

check_call([TERRAFORM_PATH, 'init'],
cwd=PROJECT_DIR,
printOut=True)
check_call([TERRAFORM_PATH, 'plan', '-no-color', '-var-file=local.tfvars', '-lock-timeout=300s', '-out', 'tfplan'],
cwd=PROJECT_DIR,
printOut=True)
check_call([TERRAFORM_PATH, 'apply', '-no-color', '-auto-approve', '-lock-timeout=300s', 'tfplan'],
cwd=PROJECT_DIR,
printOut=True)
check_call([TERRAFORM_PATH, 'output', '-json'],
cwd=PROJECT_DIR,
printOut=True)
77 changes: 77 additions & 0 deletions examples/automatic_folder/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

provider "archive" {
version = "~> 1.0"
}

provider "random" {
version = "~> 2.0"
}

provider "null" {
version = "~> 2.1"
}

data "google_projects" "in_perimeter_folder" {
filter = "parent.id:${var.folder_id}"
}

data "google_project" "in_perimeter_folder" {
count = length(data.google_projects.in_perimeter_folder.projects)

project_id = data.google_projects.in_perimeter_folder.projects[count.index].project_id
}

locals {
projects = compact(data.google_project.in_perimeter_folder.*.number)
}

module "access_context_manager_policy" {
source = "terraform-google-modules/vpc-service-controls/google"
version = "1.0.0"

parent_id = var.parent_id
policy_name = var.policy_name
}

module "access_level_members" {
source = "terraform-google-modules/vpc-service-controls/google//modules/access_level"
version = "1.0.0"

description = "${var.perimeter_name} Access Level"
policy = module.access_context_manager_policy.policy_id
name = "${var.perimeter_name}_members"
members = var.members
}

module "service_perimeter" {
source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter"
version = "1.0.0"

policy = module.access_context_manager_policy.policy_id
perimeter_name = var.perimeter_name

description = "Perimeter ${var.perimeter_name}"
resources = local.projects

access_levels = [module.access_level_members.name]
restricted_services = var.restricted_services

shared_resources = {
all = local.projects
}
}
25 changes: 25 additions & 0 deletions examples/automatic_folder/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

output "policy_name" {
description = "Name of the parent policy"
value = var.policy_name
}

output "protected_project_ids" {
description = "Project ids of the projects INSIDE the regular service perimeter"
value = local.projects
}
15 changes: 15 additions & 0 deletions examples/automatic_folder/provider.tf.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
provider "google" {
// credentials = file("credentials.json")
// project = "YOUR_PROJECT"
region = "us-central1"
}

terraform {
required_version = "~> 0.12.0"

backend "gcs" {
// credentials = "credentials.json"
bucket = "YOUR_BUCKET"
prefix = "terraform/vpc-service-controls"
}
}
56 changes: 56 additions & 0 deletions examples/automatic_folder/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

variable "project_id" {
type = string
description = "The ID of the project to which resources will be applied."
}

variable "parent_id" {
description = "The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent (ID)."
type = string
}

variable "policy_name" {
description = "The policy's name."
type = string
}

variable "members" {
description = "An allowed list of members (users, service accounts). The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid}"
type = list(string)
}

variable "folder_id" {
description = "Folder ID to watch for projects."
type = string
}

variable "perimeter_name" {
description = "Name of perimeter."
type = string
default = "regular_perimeter"
}

variable "restricted_services" {
description = "List of services to restrict."
type = list(string)
}

variable "region" {
type = string
description = "The region in which resources will be applied."
}
19 changes: 19 additions & 0 deletions examples/automatic_folder/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

terraform {
required_version = ">= 0.12"
}
Loading

0 comments on commit 36626c8

Please sign in to comment.