Skip to content

Commit

Permalink
Merge pull request #4 from MitocGroup/dev
Browse files Browse the repository at this point in the history
Decoupled and externalize `landing_zone_reader` module from `landing_zone` module
  • Loading branch information
eistrati authored Dec 27, 2019
2 parents bc2e8b9 + be24d2f commit ba416fe
Show file tree
Hide file tree
Showing 27 changed files with 1,389 additions and 24 deletions.
44 changes: 21 additions & 23 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
# Local .terraform directories
**/.terraform/*
# Node related
**/node_modules
npm-debug.log
package-lock.json

# .tfstate files
*.tfstate
*.tfstate.*
# IDEs and editors
.idea/*
.vscode/*
/venv/*
.prettierrc.json

# Crash log files
crash.log
# Local .terraform directories
**/.terraform
**/.backup
**/saml-metadata

# Ignore any .tfvars files that are generated automatically for each Terraform run. Most
# .tfvars files are managed as part of configuration and so should be included in
# version control.
#
# example.tfvars
# .tfstate files
*.tfstate*

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# .tfplan files
*.tfplan*

# Include override files you do wish to add to version control using negated pattern
#
# !example_override.tf
# .tfimport files
*.tfimport

# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*
# .tfvars files
*.tfvars
37 changes: 37 additions & 0 deletions .terrahub.yml.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
project:
name: terraform-aws-landing-zone
code: eef16dcf
include:
- .
exclude:
- .git
- node_modules
- '**/.backup'
- '**/.terraform'
terraform:
varFile:
- default.tfvars
version: 0.12.18
template:
provider:
- aws:
region: var.region
allowed_account_ids:
- var.account_id
- aws:
alias: default
region: var.region
allowed_account_ids:
- var.account_id
- aws:
alias: sample
region: var.sample_region
assume_role:
- role_arn: >-
arn:aws:iam::var.sample_account_id:role/OrganizationAccountAccessRole
session_name: var.sample_account_id
tfvars:
account_id: 123456789012
region: us-east-1
sample_account_id: 987654321098
sample_region: us-east-1
87 changes: 86 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,86 @@
# terraform-aws-landing-zone-reader
# terraform module `landing_zone_reader`
[AWS Landing Zone](https://aws.amazon.com/solutions/aws-landing-zone/) is
a solution that helps customers more quickly set up a secure, multi-account
AWS environment based on AWS best practices. This repository contains
terraform module `landing_zone_reader` that allows read-only access and
view into resources managed by terraform module `landing_zone`.

RELATED: [terraform module `landing_zone`](https://github.com/MitocGroup/terraform-aws-landing-zone)


## Usage Example
To get started, simply include the following terraform codebase:
```hcl
module "landing_zone_reader" {
source = "MitocGroup/landing-zone-reader/aws"
version = "0.0.1"
landing_zone_providers = var.landing_zone_providers
landing_zone_components = var.landing_zone_components
terraform_backend_type = var.terraform_backend_type
terraform_backend_config = var.terraform_backend_config
terraform_reader_config = var.terraform_reader_config
}
```
> NOTE: Make sure to include `variables.tf` and optionally `terraform.tfvars`
To simplify and make it easier to understand, we included default values in `terraform.tfvars`:
```hcl
landing_zone_providers = {
default = {
account_id = "123456789012"
region = "us-east-1"
}
[...]
}
landing_zone_components = {
landing_zone_vpc = "s3://terraform-aws-landing-zone/components/landing_zone_vpc/*.tfvars"
[...]
}
terraform_backend_type = "s3"
terraform_backend_config = {
bucket = "terraform-aws-landing-zone"
key = "terraform/landing_zone_reader_output/terraform.tfstate"
region = "us-east-1"
}
terraform_reader_config = true
```

> NOTE: Placeholder `[...]` from above is used to suggest that similar syntax can be added. Remove it or update in order to have valid HCL / terraform configuration.
This means that before you use this terraform module, you will need to:
1. Change `landing_zone_providers` to values that describe your AWS Organization account
* `default` reflects the default setup corresponding to AWS Organization account; add more providers by extending `landing_zone_providers` map with extra AWS accounts and/or AWS regions
* `account_id` reflects the AWS account used to deploy AWS resources; prevents provisioning AWS resources into wrong AWS account in case of valid AWS credentials
* `region` reflects the AWS region used to deploy AWS resources; create 2 different providers for the same AWS account, but different AWS regions
2. Change `landing_zone_components` to values that fit into your AWS Landing Zone use case
* each key from `landing_zone_components` map represents the name of the component from [this list of available components](https://github.com/MitocGroup/terraform-aws-landing-zone/tree/master/components)
* each value from `landing_zone_components` map represents the path to `.tfvars` file on S3 and/or local disk
* each `.tfvars` file must use HCL format; DO NOT USE other formats like JSON or YAML
3. Change `terraform_backend_type` and `terraform_backend_config` to values that reflect your terraform backend where `.tfstate` files are stored (in `variables.tf` default parameter value is defined as `local`)

### More Examples
* [Terraform module for AWS Lambda function using AWS Landing Zone reader](https://github.com/MitocGroup/terraform-aws-landing-zone-reader/tree/master/examples/example_landing_zone_reader)


## Why to Use This Solution

### No need for code changes
Terraform module for AWS Landing Zone solution is up to 10 lines of code that receives a list of `.tfvars` files as input variables which describe providers (to be read: AWS accounts and AWS regions) and configs (to be read: AWS resources)

### No need for code rewrites
This implementation engages microservices architecture, allowing any component to be replaced with another component (or multiple components)

### No need for hard-coded values
Existing AWS resources created by your team can be reused programmatically as read only values by other teams' terraform configurations

### No need to rebuild from scratch
Existing AWS resources in your current AWS account(s) can be imported and reused without downtime by this terraform module via `terraform import` command

### No need to exclude pieces of account(s) baseline
Some customers were avoiding in the past AWS Landing Zone because it doesn't support some kind of 3rd party SSO solution or 3rd party Logging solution. By using terraform, we can easily bring those solutions into AWS Landing Zone as a set of components and empower customers to continue using best practices of both worlds

### Additionally, this module helps enforce best practices
- By removing the need for access to AWS root account(s)
- By using IAM cross-account roles and/or STS temporary credentials
- By enabling centralized CloudTrail logs and cross-region replication of CloudTrail logs
- By empowering complex organizations to separate roles and responsibilities (e.g. InfoSec team can place explicit deny on IAM, VPC, SG and STS for other teams and/or other environments like production or pre-production)
12 changes: 12 additions & 0 deletions components/landing_zone_reader_output/.terrahub.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
component:
name: landing_zone_reader_output
template:
resource:
null_resource:
everytime:
triggers:
timestamp: timestamp()
output:
landing_zone_reader:
value: >-
merge(map("ids", "test"))
2 changes: 2 additions & 0 deletions components/landing_zone_reader_output/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# landing_zone_reader_output
Component for AWS Landing Zone
6 changes: 6 additions & 0 deletions data.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
data "terraform_remote_state" "landing_zone_reader_output" {
depends_on = [module.landing_zone_reader_config]

backend = var.terraform_backend_type
config = var.terraform_backend_output
}
97 changes: 97 additions & 0 deletions examples/example_landing_zone_reader/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# example_landing_zone_reader
This example implements fully functional terraform configuration that can create (or update) `hello_world` AWS Lambda function using subnets and security groups programmatically retrieved by `landing_zone_reader` module.

## Example Usage

This module can be used as easy as:
```hcl
module "example_landing_zone_reader" {
source = "MitocGroup/landing-zone-reader/aws"
terraform_backend_type = var.terraform_backend_type
terraform_backend_config = var.terraform_backend_config
terraform_reader_config = var.terraform_reader_config
landing_zone_providers = var.landing_zone_providers
landing_zone_components = var.landing_zone_components
}
```

For easier usage and reduced complexity, we recommend to define `locals` as shown below:
```hcl
locals {
landing_zone_iam_role_arns = module.example_landing_zone_reader.landing_zone_reader["landing_zone_iam_role_arns"]
landing_zone_subnet_ids = module.example_landing_zone_reader.landing_zone_reader["landing_zone_subnet_ids"]
landing_zone_security_group_ids = module.example_landing_zone_reader.landing_zone_reader["landing_zone_security_group_ids"]
}
```

Use above defined `locals` in your terraform resource as easy as:
```hcl
resource "aws_lambda_function" "hello_world" {
function_name = "my_hello_world"
description = "Managed by TerraHub"
runtime = "nodejs10.x"
handler = "index.handler"
memory_size = "128"
timeout = "30"
role = local.landing_zone_iam_role_arns["default"]["ServiceRoleForLambdaEdge"]
s3_bucket = "www.terrahub.io"
s3_key = "/hello-world/nodejs10.x.zip"
vpc_config = {
subnet_ids = values(local.landing_zone_subnet_ids["default"])
security_group_ids = values(local.landing_zone_security_group_ids["default"])
}
}
```

## Input / Arguments Reference
The following arguments / input parameters are expected by AWS Lambda function resource:

Name | Required? | Description
-----|-----------|------------
source | Required | All modules require a source argument, which is a meta-argument defined by Terraform CLI. Its value is either the path to a local directory of the module's configuration files, or a remote module source that Terraform should download and use. This value must be a literal string with no template sequences; interpolations are not allowed.
version | Optional | A version constraint string that specifies which versions of the referenced module are acceptable. The newest version matching the constraint will be used. version is supported only for modules retrieved from module registries.
providers | Optional | A map whose keys are provider configuration names that are expected by child module and whose values are corresponding provider names in the calling module. This allows provider configurations to be passed explicitly to child modules. If not specified, the child module inherits all of the default (un-aliased) provider configurations from the calling module.
filename | Optional | The path to the function's deployment package within the local filesystem. If defined, The s3_-prefixed options cannot be used.
s3_bucket | Optional | The S3 bucket location containing the function's deployment package. Conflicts with filename. This bucket must reside in the same AWS region where you are creating the Lambda function.
s3_key | Optional | The S3 key of an object containing the function's deployment package. Conflicts with filename.
s3_object_version | Optional | The object version containing the function's deployment package. Conflicts with filename.
function_name | Required | A unique name for your Lambda Function.
dead_letter_config | Optional | Nested block to configure the function's dead letter queue. See details below.
handler | Required | The function entrypoint in your code.
role | Required | IAM role attached to the Lambda Function. This governs both who / what can invoke your Lambda Function, as well as what resources our Lambda Function has access to. See Lambda Permission Model for more details.
description | Optional | Description of what your Lambda Function does.
layers | Optional | List of Lambda Layer Version ARNs (maximum of 5) to attach to your Lambda Function. See Lambda Layers
memory_size | Optional | Amount of memory in MB your Lambda Function can use at runtime. Defaults to 128. See Limits
runtime | Required | See Runtimes for valid values.
timeout | Optional | The amount of time your Lambda Function has to run in seconds. Defaults to 3. See Limits
reserved_concurrent_executions | Optional | The amount of reserved concurrent executions for this lambda function. A value of 0 disables lambda from being triggered and -1 removes any concurrency limitations. Defaults to Unreserved Concurrency Limits -1. See Managing Concurrency
publish | Optional | Whether to publish creation/change as new Lambda Function Version. Defaults to false.
vpc_config | Optional | Provide this to allow your function to access your VPC. Fields documented below. See Lambda in VPC
environment | Optional | The Lambda environment's configuration settings. Fields documented below.
kms_key_arn | Optional | The ARN for the KMS encryption key.
source_code_hash | Optional | Used to trigger updates. Must be set to a base64-encoded SHA256 hash of the package file specified with either filename or s3_key. The usual way to set this is filebase64sha256("file.zip") (terraform 0.11.12 and later) or base64sha256(file("file.zip")) (terraform 0.11.11 and earlier), where "file.zip" is the local filename of the lambda function source archive.
tags | Optional | A mapping of tags to assign to the object.
dead_letter_config | |
target_arn | Required | The ARN of an SNS topic or SQS queue to notify when an invocation fails. If this option is used, the function's IAM role must be granted suitable access to write to the target object, which means allowing either the sns:Publish or sqs:SendMessage action on this ARN, depending on which service is targeted.
tracing_config | |
mode | Required | Can be either PassThrough or Active. If PassThrough, Lambda will only trace the request from an upstream service if it contains a tracing header with "sampled=1". If Active, Lambda will respect any tracing header it receives from an upstream service. If no tracing header is received, Lambda will call X-Ray for a tracing decision.
vpc_config | |
subnet_ids | Required | A list of subnet IDs associated with the Lambda function.
security_group_ids | Required | A list of security group IDs associated with the Lambda function.
environment | |
variables | Optional | A map that defines environment variables for the Lambda function.

## Output / Attributes Reference
The following attributes / output parameters are produced by AWS Lambda function resource:

Name | Description
-----|------------
landing_zone_reader | This map ( to be exact: map(map(map(any))) ) consolidates all outputs for each AWS resource created (or updated) in each AWS account (defined by `provider`) for each landing zone component (defined by `landing_zone_components`)
arn | The Amazon Resource Name (ARN) identifying your Lambda Function.
qualified_arn | The Amazon Resource Name (ARN) identifying your Lambda Function Version (if versioning is enabled via publish = true).
invoke_arn | The ARN to be used for invoking Lambda Function from API Gateway - to be used in aws_api_gateway_integration's uri
version | Latest published version of your Lambda Function.
last_modified | The date this resource was last modified.
source_code_hash | Base64-encoded representation of raw SHA-256 sum of the zip file, provided either via filename or s3_* parameters.
source_code_size | The size in bytes of the function .zip file.
5 changes: 5 additions & 0 deletions examples/example_landing_zone_reader/locals.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
locals {
landing_zone_iam_role_arns = module.example_landing_zone_reader.landing_zone_reader["landing_zone_iam_role_arns"]
landing_zone_subnet_ids = module.example_landing_zone_reader.landing_zone_reader["landing_zone_subnet_ids"]
landing_zone_security_group_ids = module.example_landing_zone_reader.landing_zone_reader["landing_zone_security_group_ids"]
}
25 changes: 25 additions & 0 deletions examples/example_landing_zone_reader/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module "example_landing_zone_reader" {
source = "MitocGroup/landing-zone-reader/aws"
landing_zone_providers = var.landing_zone_providers
landing_zone_components = var.landing_zone_components
terraform_backend_type = var.terraform_backend_type
terraform_backend_config = var.terraform_backend_config
terraform_reader_config = var.terraform_reader_config
}

resource "aws_lambda_function" "hello_world" {
function_name = "my_hello_world"
description = "Managed by TerraHub"
runtime = "nodejs10.x"
handler = "index.handler"
memory_size = "128"
timeout = "30"
role = local.landing_zone_iam_role_arns["default"]["ServiceRoleForLambdaEdge"]
s3_bucket = "www.terrahub.io"
s3_key = "hello-world/nodejs10.x.zip"

vpc_config {
security_group_ids = values(local.landing_zone_security_group_ids["default"])
subnet_ids = values(local.landing_zone_subnet_ids["default"])
}
}
59 changes: 59 additions & 0 deletions examples/example_landing_zone_reader/output.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
output "landing_zone_vpc_ids" {
value = lookup(module.example_landing_zone_reader.landing_zone_reader, "landing_zone_vpc_ids", "Is not set!")
description = "The ID's of the VPC."
}

output "landing_zone_vpc_arns" {
value = lookup(module.example_landing_zone_reader.landing_zone_reader, "landing_zone_vpc_arns", "Is not set!")
description = "The ARN's of the VPC."
}

output "landing_zone_subnet_ids" {
value = lookup(module.example_landing_zone_reader.landing_zone_reader, "landing_zone_subnet_ids", "Is not set!")
description = "The ID's of the Subnet."
}

output "landing_zone_subnet_availability_zone_ids" {
value = lookup(module.example_landing_zone_reader.landing_zone_reader, "landing_zone_subnet_availability_zone_ids", "Is not set!")
description = "The Availability Zone ID's of the Subnet."
}

output "landing_zone_subnet_arns" {
value = lookup(module.example_landing_zone_reader.landing_zone_reader, "landing_zone_subnet_arns", "Is not set!")
description = "The ARN's of the Subnet."
}

output "arn" {
value = aws_lambda_function.hello_world.arn
description = "The Amazon Resource Name (ARN) identifying your Lambda Function."
}

output "qualified_arn" {
value = aws_lambda_function.hello_world.qualified_arn
description = "The Amazon Resource Name (ARN) identifying your Lambda Function Version (if versioning is enabled via publish = true)."
}

output "invoke_arn" {
value = aws_lambda_function.hello_world.invoke_arn
description = "The ARN to be used for invoking Lambda Function from API Gateway - to be used in aws_api_gateway_integration's uri"
}

output "version" {
value = aws_lambda_function.hello_world.version
description = "Latest published version of your Lambda Function."
}

output "last_modified" {
value = aws_lambda_function.hello_world.last_modified
description = "The date this resource was last modified."
}

output "source_code_hash" {
value = aws_lambda_function.hello_world.source_code_hash
description = "Base64-encoded representation of raw SHA-256 sum of the zip file, provided either via filename or s3_* parameters."
}

output "source_code_size" {
value = aws_lambda_function.hello_world.source_code_size
description = "The size in bytes of the function .zip file."
}
3 changes: 3 additions & 0 deletions examples/example_landing_zone_reader/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
provider "aws" {
region = "us-east-1"
}
Loading

0 comments on commit ba416fe

Please sign in to comment.