Skip to content

DOPS-101 Add trussworks bootstrap to devops example #64

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
.DS_Store
.vagrant
/env.sh
/terraform/.terraform*
/terraform/terraform.tfstate*
/terraform/tf.plan
/terraform/**/.terraform
/terraform/**/terraform.tfstate*
/terraform/**/tf.plan
__pycache__
build/
tmp/
Expand Down
26 changes: 10 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ See the branch [demo-20180619](https://github.com/ModusCreateOrg/devops-infra-de
See the branch [demo-20180926](https://github.com/ModusCreateOrg/devops-infra-demo/tree/demo-20180926) for the code for the demo for the [Continuous Delivery NYC talk _Managing Expensive or Destructive Operations in Jenkins CI_](https://www.meetup.com/ContinuousDeliveryNYC/events/254036209/). Slides from this presentation are on [SlideShare](https://www.slideshare.net/RichardBullingtonMcG/managing-expensive-or-destructive-operations-in-jenkins-ci).

See the branch [demo-20181205](https://github.com/ModusCreateOrg/devops-infra-demo/tree/demo-20181205) for the code for the demo for the [Ansible NYC talk _Ansible Image Bakeries: Best Practices & Pitfalls_](https://www.meetup.com/Ansible-NYC/events/256728741/). Slides from this presentation are on [SlideShare](https://www.slideshare.net/RichardBullingtonMcG/ansible-image-bakeries-best-practices-and-pitfalls).

See the branch [demo-20190130](https://github.com/ModusCreateOrg/devops-infra-demo/tree/demo-20190130) for the code for the demo for the [Big Apple DevOps talk _Monitoring and Alerting as code with Terraform and New Relic_](https://www.meetup.com/Big-Apple-DevOps/events/257744262/). Slides from this presentation are on [Slideshare](https://www.slideshare.net/RichardBullingtonMcG/monitoring-and-alerting-as-code-with-terraform-and-new-relic).

See the branch [demo-20191109](https://github.com/ModusCreateOrg/devops-infra-demo/tree/demo-20191109) for the code for the demo for the [BSidesCT 2019 talk _Extensible DevSecOps pipelines with Jenkins, Docker, Terraform, and a kitchen sink full of scanners_](https://bsidesct.org/schedule/). Slides from this presentation are on
[Slideshare](https://www.slideshare.net/RichardBullingtonMcG/extensible-dev-secops-pipelines-with-jenkins-docker-terraform-and-a-kitchen-sink-full-of-scanners)

Instructions
------------

To run the demo end to end, you will need:

* [AWS Account](https://aws.amazon.com/)
* [Google Cloud Account](https://cloud.google.com/)
* [Docker](https://docker.com/) (tested with 18.05.0-ce)
Expand All @@ -33,7 +33,7 @@ Instructions

Optionally, you can use Vagrant to test ansible playbooks locally and Jenkins to orchestrate creation of AMIs in conjunction with GitHub branches and pull requests.

You will also need to set a few environment variables. The method of doing so will vary from platform to platform.
You will also need to set a few environment variables. The method of doing so will vary from platform to platform.

```
AWS_PROFILE
Expand Down Expand Up @@ -74,20 +74,14 @@ Install [Vagrant](https://www.vagrantup.com/). Change directory into the root of

### Terraform

This Terraform setup stores its state in Amazon S3 and uses DynamoDB for locking. There is a bit of setup required to bootstrap that configuration. You can use [this repository](https://github.com/monterail/terraform-bootstrap-example) to use Terraform to do that bootstrap process. The `backend.tfvars` file in that repo should be modified as follows to work with this project:
This Terraform setup stores its state in Amazon S3 and uses DynamoDB for locking. There is a bit of setup required to bootstrap the configuration. Check out `./terraform/bootstrap/README.md` to setup the resources required for backend.

If you override the default input values (by CLI or using a `.tfvars` file) for bootstrapping, please update the `terraform.backend` section in `./terraform/terraform.tf` to reflect that.

(Replace us-east-1 and XXXXXXXXXXXX with the AWS region and your account ID)
```
bucket = "tf-state.devops-infra-demo.us-east-1.XXXXXXXXXXXX"
dynamodb_table = "TerraformStatelock-devops-infra-demo"
key = "terraform.tfstate"
profile = "terraform"
region = "us-east-1"
```
You'll also need to modify the list of operators who can modify the object in the S3 bucket. Put in the IAM user names of the user into the `setup/variables.tf` file in that project. If your Jenkins instance uses an IAM role to grant access, give it a similar set of permissions to those granted on in the bucket policy to IAM users.

These commands will then set up cloud resources using terraform:

cd terraform
terraform init
terraform get
Expand All @@ -112,7 +106,7 @@ The application loads an image from Google storage. To get it loading correctly,

### Auto Scaling Groups

The application in this demo uses an AWS Auto Scaling Group in order to dynamically change the number of servers deployed in response to load. Two policies help guide how many instances are available: a CPU scaling policy that seeks to keep the average CPU load below 40% in the cluster, and a scheduled scaling policy that scales the entire cluster down to 0 instances at 02:00 UTC every night, to minimize the charges should you forget to destroy the cluster. If the cluster is scaled down to 0 instances, you will need to edit the Auto Scaling Group through the console, the CLI, or an API call to set the sizes to non-zero, for example
The application in this demo uses an AWS Auto Scaling Group in order to dynamically change the number of servers deployed in response to load. Two policies help guide how many instances are available: a CPU scaling policy that seeks to keep the average CPU load below 40% in the cluster, and a scheduled scaling policy that scales the entire cluster down to 0 instances at 02:00 UTC every night, to minimize the charges should you forget to destroy the cluster. If the cluster is scaled down to 0 instances, you will need to edit the Auto Scaling Group through the console, the CLI, or an API call to set the sizes to non-zero, for example

### Elastic Load Balancing

Expand Down
4 changes: 2 additions & 2 deletions ansible/bakery.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
hosts: 127.0.0.1
connection: local
become: yes
roles:
roles:
- cloudwatch-agent

- name: Install New Relic Infrastructure
hosts: 127.0.0.1
connection: local
become: yes
roles:
roles:
- newrelic.newrelic-infra
vars:
nrinfragent_os_name: CentOS
Expand Down
4 changes: 2 additions & 2 deletions ansible/requirements.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# Install roles from Ansible Galaxy

# CIS hardening
- src: MindPointGroup.RHEL7-CIS
- src: mindpointgroup.rhel7_cis

# NGINX web server
- src: nginxinc.nginx
version: 0.14.0

# AWS CodeDeploy agent
# The original version of this role in Ansible Galaxy
Expand All @@ -17,4 +18,3 @@

# New Relic Infrastructure
- src: newrelic.newrelic-infra

5 changes: 5 additions & 0 deletions atlantis.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
# This does not use Atlantis (https://www.runatlantis.io/) to manage Terraform,
# so disable it with an empty list of projects.
version: 3
projects:
4 changes: 3 additions & 1 deletion bin/ansible.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Run ansible
# Run ansible
#
# Set bash unofficial strict mode http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -euo pipefail
Expand All @@ -20,5 +20,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

ensure_not_root

sudo yum -y install yum-utils pcre2

cd "$DIR/../ansible"
ansible-playbook -l localhost "$@"
6 changes: 2 additions & 4 deletions bin/common-terraform.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,8 @@ function init_terraform() {
#shellcheck disable=SC2086,SC2046
$DOCKER_TERRAFORM init \
-input="$INPUT_ENABLED" \
-backend-config bucket=tf-state.${PROJECT_NAME}.${AWS_DEFAULT_REGION}.$(get_aws_account_id) \
-backend-config dynamodb_table=TerraformStatelock-${PROJECT_NAME} \
-backend-config region=${AWS_DEFAULT_REGION} \
-backend-config encrypt=true
-backend-config region=${AWS_DEFAULT_REGION}

# Generate an SSH keypair if none exists yet
if [[ ! -f ~/.ssh/id_rsa.pub ]]; then
#shellcheck disable=SC2174
Expand Down
3 changes: 2 additions & 1 deletion bin/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function get_env_tmpfile() {
local TMPFILE
TMPFILE="$(mktemp)"
grep ^export "$DIR/../env.sh" | cut -c8- > "$TMPFILE"
printenv | grep '^AWS' >> "$TMPFILE"
echo "$TMPFILE"
}

Expand Down Expand Up @@ -66,7 +67,7 @@ function get_docker_packer {
PACKER_AWS_VPC_ID="$(curl --silent http://169.254.169.254/latest/meta-data/network/interfaces/macs/"$INTERFACE"/vpc-id)"
fi

echo "docker run -i
echo "docker run -i --rm
${USE_TTY}
--env-file $TMPFILE
-e PACKER_AWS_SUBNET_ID=$PACKER_AWS_SUBNET_ID
Expand Down
2 changes: 2 additions & 0 deletions bin/install-ansible.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
#shellcheck disable=SC1090
. "$DIR/common.sh"

ls -al /etc/yum.repos.d

ensure_not_root

quick_yum_install epel-release
Expand Down
18 changes: 9 additions & 9 deletions bin/validate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# Set bash unofficial strict mode http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -euo pipefail

# Set DEBUG to true for enhanced debugging: run prefixed with "DEBUG=true"
${DEBUG:-false} && set -vx
# Credit to https://stackoverflow.com/a/17805088
Expand All @@ -27,22 +27,22 @@ echo "Linting packer files"
$DOCKER_PACKER validate app/packer/machines/web-server.json

# Ensure that `terraform fmt` comes up clean
if [[ "$SKIP_TERRAFORM" == "false" ]]; then
echo "Linting terraform files for correctness"
if [[ "${SKIP_TERRAFORM:-false}" == "false" ]]; then
DOCKER_TERRAFORM=$(get_docker_terraform)
init_terraform
$DOCKER_TERRAFORM validate \
-var 'newrelic_license_key=ZZZZ' \
-var 'newrelic_api_key=ZZZZ' \
-var '[email protected]' \
echo "Linting terraform files for formatting"
fmt=$($DOCKER_TERRAFORM fmt)
echo "Linting terraform files for formatting"
if [[ -n "$fmt" ]]; then
echo 'ERROR: these files are not formatted correctly. Run "terraform fmt"'
echo "$fmt"
git diff
exit 1
fi
echo "Linting terraform files for correctness"
init_terraform
$DOCKER_TERRAFORM validate \
-var 'newrelic_license_key=ZZZZ' \
-var 'newrelic_api_key=ZZZZ' \
-var '[email protected]'
fi

echo "Linting shell scripts"
Expand Down
22 changes: 22 additions & 0 deletions bootstrap/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 44 additions & 0 deletions bootstrap/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Modus Devops Demo Bootstrap

This terraform module is used to bootstrap the backend for the `/terraform` project. It uses [trussworks/bootstrap/aws](https://github.com/trussworks/terraform-aws-bootstrap) module to create all the resources needed to enable terraform backend in AWS.

## How to use

```bash
cd bootstrap
terraform init
terraform apply
```

This will generate output which looks like this:

```
backend_details = {
"dynamodb_table" = "moduscreate-devops-demo-state-lock"
"logging_bucket" = "moduscreate-devops-demo-tf-state-log-us-east-1"
"state_bucket" = "moduscreate-devops-demo-tf-state-us-east-1"
}
```

It can then be used in the `terraform.backend` config of main project (not the bootstrap project).

```terraform
terraform {
# ... existing config

backend "s3" {
bucket = "moduscreate-devops-demo-tf-state-us-east-1"
key = "terraform-state.tfstate"
dynamodb_table = "moduscreate-devops-demo-state-lock"
region = "us-east-1"
encrypt = "true"
}
}
```

## Inputs

| Name | Description | Default |
|-----------------|------------------------------|-------------------------|
| `aws_region` | Amazon region to use | us-east-1 |
| `account_alias` | Prefix for backend resources | moduscreate-devops-demo |
44 changes: 44 additions & 0 deletions bootstrap/jenkins.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
We use jenkins to automate deployment with Terraform. Jenkins
is set up in a different AWS account.

This group of IAM resources allow jenkins to assume a role needed
to deploy resources (and make changes to backend).
*/

data "aws_iam_policy_document" "terraform_backend_account_policy" {
statement {
effect = "Allow"

principals {
type = "AWS"
identifiers = ["arn:aws:iam::191447213457:role/jenkins-role"]
}

actions = ["sts:AssumeRole"]
}
}

resource "aws_iam_role" "terraform_backend_role" {
name = "terraform_sandbox_backend_admin"
assume_role_policy = data.aws_iam_policy_document.terraform_backend_account_policy.json
}

data "aws_iam_policy_document" "terraform_backend_role_policy_document" {
statement {
effect = "Allow"

actions = ["*"]
resources = ["*"]
}
}

resource "aws_iam_policy" "terraform_backend_role_policy" {
name = "terraform-backend-role-policy"
policy = data.aws_iam_policy_document.terraform_backend_role_policy_document.json
}

resource "aws_iam_role_policy_attachment" "terraform_backend_attachment" {
role = aws_iam_role.terraform_backend_role.name
policy_arn = aws_iam_policy.terraform_backend_role_policy.arn
}
39 changes: 39 additions & 0 deletions bootstrap/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.27"
}
}
}

provider "aws" {
region = var.aws_region
}

module "bootstrap" {
source = "trussworks/bootstrap/aws"

region = var.aws_region
account_alias = var.account_alias
dynamodb_table_name = "${var.account_alias}-state-lock"
}

data "aws_caller_identity" "current" {}

output "account_id" {
value = data.aws_caller_identity.current.account_id
}

output "arn" {
value = data.aws_caller_identity.current.arn
}

output "user_id" {
value = data.aws_caller_identity.current.user_id
}

output "backend_details" {
description = "Details of the S3 bucket and DynamoDB tables created for backend"
value = module.bootstrap
}
Loading