Skip to content

Commit

Permalink
Uptake current work (#91)
Browse files Browse the repository at this point in the history
* add the shellcheck extension to the devcontainer (#70)

Co-authored-by: Glenn Musa <glennmusa@users.noreply.github.com>

* Enable Azure Security Center in MLZ subscriptions (#55)

* Updated documentation in script

* - Added execution flag to shell scripts
- Added configure_asc.sh script
- Added code to create subs array to mlz_tf_setup.sh
- Added code to call configure_asc.sh from mlz_tf_setup.sh

* - Added executable flag to unzipprovider.sh script

* - Added executable flag to check scripts

* - Updated loop code for workspace setting

* - Created folder for ASC scripts
- Copied generate_names.sh script into ASC folder

* - Removed ASC calling code from setup script
- Updated naming in asc script
- Added ASC naming to generate script

* - Moved LAWS name generation into generate names
- Moved generate names call into loop
- Updated wait loop to use variables

* - Removed message for elapsed time
- Added quotes consistently for echo's

* - Updated Copyright statement
- Added set -e
- Modified variables to use env & location from vars file

* - Corrected counter logic
- Corrected description in names script

* output number of attempts remaining

* add a comment that this may fail

* - Removed creater comment

* unusually typo

Co-authored-by: Glenn Musa <glennmusa@users.noreply.github.com>

* Add persona and business justification to the Issue template (#73)

* issue template update

* Implement error handling into shell scripts (#72)

* - Added error handling to configure_asc script
- Suppressed "create" output in configure_asc script

* - Added error handling to config_create script
- Remove commented lines from configure_asc script

* - Added error handling to config_validate script

* - Added error handling to get_sp_identity script
- Corrected SP lookup

* - Added error handling to mlz_config_create script

* - Updated echo lines

* Implement Sub ID array for Role assignment (#76)

* - Updated bullet numbering in README
- Added code to create sub id array

* - Reverted numbering changes made to README.md

* - Added description of sed command

* - Added parameter to suppress WARNING on sp create

* Remove providers (#84)

* use azurerm 2.50 in src/core

* removing provider files

* updates to readme and scripts for local providers

* set tf provider folder

Co-authored-by: Glenn Musa <glennmusa@users.noreply.github.com>

* unique diagnostic settings names (#82)

Co-authored-by: Glenn Musa <glennmusa@users.noreply.github.com>

* move scripts to src/scripts (#87)

* add a workflow for apply and destroy terraform (#83)

* add retries for apply and destroy

* update readmes

Co-authored-by: Glenn Musa <4622125+glennmusa@users.noreply.github.com>
Co-authored-by: Glenn Musa <glennmusa@users.noreply.github.com>
Co-authored-by: Byron Boudreaux <16844071+Phydeauxman@users.noreply.github.com>
Co-authored-by: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com>
Co-authored-by: Breanna-Stryker <>
  • Loading branch information
5 people authored Mar 19, 2021
1 parent b2a455c commit b8114aa
Show file tree
Hide file tree
Showing 32 changed files with 596 additions and 202 deletions.
16 changes: 16 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ RUN wget -O terraform.zip https://releases.hashicorp.com/terraform/0.13.4/terraf
&& unzip ./terraform.zip -d /usr/local/bin/ \
&& rm terraform.zip

# Download Terraform providers (plugins)
# Setting the TF_PLUGIN_CACHE_DIR environment variable instructs Terraform to search that folder for plugins first
ENV TF_PLUGIN_CACHE_DIR=/usr/lib/tf-plugins
ARG AZURERM_LOCAL_PATH="${TF_PLUGIN_CACHE_DIR}/registry.terraform.io/hashicorp/azurerm/2.50.0/linux_amd64"
ARG RANDOM_LOCAL_PATH="${TF_PLUGIN_CACHE_DIR}/registry.terraform.io/hashicorp/random/3.1.0/linux_amd64"
ARG AZURERM_PROVIDER=https://releases.hashicorp.com/terraform-provider-azurerm/2.50.0/terraform-provider-azurerm_2.50.0_linux_amd64.zip
ARG RANDOM_PROVIDER=https://releases.hashicorp.com/terraform-provider-random/3.1.0/terraform-provider-random_3.1.0_linux_amd64.zip
RUN wget -O azurerm.zip ${AZURERM_PROVIDER} \
&& wget -O random.zip ${RANDOM_PROVIDER} \
&& mkdir -p ${AZURERM_LOCAL_PATH} \
&& mkdir -p ${RANDOM_LOCAL_PATH} \
&& unzip azurerm.zip -d ${AZURERM_LOCAL_PATH} \
&& unzip random.zip -d ${RANDOM_LOCAL_PATH} \
&& rm azurerm.zip \
&& rm random.zip

# Install the Microsoft package key
RUN wget -q https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \
&& dpkg -i packages-microsoft-prod.deb \
Expand Down
3 changes: 3 additions & 0 deletions .github/ISSUE_TEMPLATE/issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ assignees: ''

---

**Benefit/Result/Outcome**
*Write a single statement describing the value someone would receive when this issue is completed. Consider adding a persona label to define the role that will benefit, e.g. 'persona: IT admin'.*

**Description**


Expand Down
84 changes: 84 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# workflows

These are the automated workflows we use for ensuring a quality working product.

For more on GitHub Actions: <https://docs.github.com/en/actions/>

For more on workflows: <https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions/>

## Contents

- apply-and-destroy-terraform.yml

This workflow assumes some pre-requisites have been set-up. See: [Configuration Prerequisites](#Configuration-Prerequisites)

1. Checks out the .devcontainer from a private container registry for common tools

1. Authenticates against a pre-configured KeyVault that contains
- values for authenticating against a storage account
- values for deploying terraform

1. Pulls known good MLZ and Terraform configuration variables from that storage account

1. Applies terraform anew from that configuration (see [build/README.md](../../build/README.md) for how this works)

1. Destroys terraform from that configuration (see [build/README.md](../../build/README.md) for how this works)

- validate-terraform.yml

1. Checks out the .devcontainer from a private container registry for common tools

1. Recursively validates and lints all the terraform referenced at src/core

## Configuration Prerequisites

1. MLZ Setup

To apply terraform at all, locally, or from this automation, `scripts/mlz_tf_setup.sh` must be run to create the storage accounts to store Terraform state and create the Service Principal with authorization to deploy resources into the configured subscription(s).

See the root README's [Configure the Terraform Backend](#../..//README.md/#Configure-the-Terraform-Backend) on how to do this.

1. Configuration store

When applying terraform locally or from this automation, an MLZ Configuration file (commonly mlz_tf_cfg.var) and Terraform-specific variables files (commonly *.tfvars) are required.

You should end up with a container with these files:

File Name | Value
------------ | -------------
mlz_tf_cfg.var | An MLZ Configuration file that comes from mlz_tf_setup.sh
globals.tfvars | Global MLZ terraform values
saca-hub.tfvars | SACA Hub MLZ terraform values
tier-0.tfvars | Tier 0 MLZ terraform values
tier-1.tfvars | Tier 1 MLZ terraform values
tier-2.tfvars | Tier 2 MLZ terraform values

Running this from your local machine, you can provide these files yourself, but, today, for automation these files are stored in an Azure Storage Account and retrieved at workflow execution time. See [build/get_vars.sh](../../build/get_vars.sh) to see how we retrieve

```plaintext
./build/get_vars.sh
# pulls down these files:
vars/mlz_tf_cfg.var
vars/globals.tfvars
vars/saca-hub.tfvars
vars/tier-0.tfvars
vars/tier-1.tfvars
vars/tier-2.tfvars
```

1. Secret store and minimally scoped Service Principal

See [glennmusa/keyvault-for-actions](https://github.com/glennmusa/keyvault-for-actions) to create a minimally scoped Service Principal to pull sensitive values from an Azure Key Vault.

Supply that Key Vault the values for:

Secret Name | Value
------------ | -------------
MLZCLIENTID | The Service Principal Authorized to deploy resources into MLZ Terraform Subscriptions
MLZCLIENTSECRET | The credential for the Service Principal above
STORAGEACCOUNT | The Azure Storage Account for the files in the previous step
STORAGECONTAINER | The container contianing the files in the previous step
STORAGETOKEN | A token to access the storage account (we used a Container SAS)

For more on creating a minimally scoped token to access storage see: <https://docs.microsoft.com/en-us/azure/storage/common/storage-sas-overview/>
60 changes: 60 additions & 0 deletions .github/workflows/apply-and-destroy-terraform.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

name: apply-and-destroy-terraform
on: [workflow_dispatch]
jobs:
apply-and-destroy-terraform:
runs-on: ubuntu-latest

container:
image: acrmlzcicd.azurecr.io/missionlzdev
credentials:
username: ${{ secrets.acr_username }}
password: ${{ secrets.acr_password }}

steps:
- uses: actions/checkout@v2

- uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

- uses: Azure/get-keyvault-secrets@v1
with:
keyvault: ${{ secrets.KEY_VAULT_NAME }}
secrets: '*'

- name: get vars
run : |
cd build
./get_vars.sh
- name: login
run : |
cd build
./login_azcli.sh vars/mlz_tf_cfg.var
- name: apply terraform
run : |
cd build
./apply_tf.sh \
vars/mlz_tf_cfg.var \
vars/globals.tfvars \
vars/saca-hub.tfvars \
vars/tier-0.tfvars \
vars/tier-1.tfvars \
vars/tier-2.tfvars \
n
- name: destroy terraform
run : |
cd build
./destroy_tf.sh \
vars/mlz_tf_cfg.var \
vars/globals.tfvars \
vars/saca-hub.tfvars \
vars/tier-0.tfvars \
vars/tier-1.tfvars \
vars/tier-2.tfvars \
n
7 changes: 6 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ This section describes the guidelines for submitting issues.

### Issue Types

- Issue: Describe work that the team will do to enhance the product. Issues contain the elements of a user story:
- The title summarizes the work to be done.
- A benefit, reason, or outcome is entered as a single statement to describe why completing this issue adds value, and which role/persona will benefit.
- A label for the persona describing the person who will receive the benefit or outcome.
- A general description adds more context and details.
- Acceptance criteria describe specific details which tell us when the issue has been completed.
- Question: Ask a question about how the system works, ask for feedback on an idea about how the system could work, or you ran into something unexpected and you want guidance.
- Feature Request: Suggest a new idea for the project.
- Issue: Describe work that the team will do to enhance the product.
- Bug: Report a defect.

### Before You File
Expand Down
40 changes: 16 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,10 @@ Terraform resources to deploy Tier 0, 1, and 2, and the components of a [SACA hu
az login
```

1. [Prepare the Terraform provider cache](#Prepare-the-Terraform-provider-cache)
1. [Configure the Terraform Backend](#Configure-the-Terraform-Backend)
1. [Set Terraform Configuration Variables](#Set-Terraform-Configuration-Variables)
1. [Deploy Terraform Configuration](#Deploy-Terraform-Configuration)

### Prepare the Terraform provider cache

We source the terraform provider locally from this repository and circumvent the need to fetch it from the internet.

This below script will unzip the provider from the /src/provider_archive folder and place the provider in the /src/provider_cache folder and set execute permissions for the current user.

Execute `unzipprovider.sh`

```bash
chmod u+x src/provider_archive/unzipprovider.sh
src/provider_archive/unzipprovider.sh
```

### Configure the Terraform Backend

The MLZ deployment architecture uses a single Service Principal whose credentials are stored in a central "config" Key Vault. Terraform state storage is distributed into a separate storage account for each tier. When deploying the MLZ architecture, all tiers can be deployed into a single subscription or each tier can be deployed into its own subscription.
Expand All @@ -50,7 +36,7 @@ The MLZ deployment architecture uses a single Service Principal whose credential
mlz_config_location="eastus"
```

1. Run `mlz_tf_setup.sh` at [scripts/mlz_tf_setup.sh](scripts/mlz_tf_setup.sh) to create:
1. Run `mlz_tf_setup.sh` at [src/scripts/mlz_tf_setup.sh](src/scripts/mlz_tf_setup.sh) to create:

- A config Resource Group to store the Key Vault
- Resource Groups for each tier to store the Terraform state Storage Account
Expand All @@ -62,9 +48,9 @@ The MLZ deployment architecture uses a single Service Principal whose credential
```bash
# usage mlz_tf_setup.sh: <mlz_tf_cfg.var path>
chmod u+x scripts/mlz_tf_setup.sh
chmod u+x src/scripts/mlz_tf_setup.sh
scripts/mlz_tf_setup.sh src/core/mlz_tf_cfg.var
src/scripts/mlz_tf_setup.sh src/core/mlz_tf_cfg.var
```
### Set Terraform Configuration Variables
Expand All @@ -87,43 +73,49 @@ location="eastus" # the value used by Terraform in src/core/globals.tfvars
### Deploy Terraform Configuration
You can use `apply_terraform.sh` at [scripts/apply_terraform.sh](scripts/apply_terraform.sh) to both initialize Terraform and apply a Terraform configuration based on the backend environment variables and Terraform variables you've setup in previous steps.
You can use `apply_terraform.sh` at [src/scripts/apply_terraform.sh](src/scripts/apply_terraform.sh) to both initialize Terraform and apply a Terraform configuration based on the backend environment variables and Terraform variables you've setup in previous steps.

The script `destroy_terraform.sh` at [scripts/destroy_terraform.sh](scripts/destroy_terraform.sh) is helpful during testing. This script is exactly like the
The script `destroy_terraform.sh` at [src/scripts/destroy_terraform.sh](src/scripts/destroy_terraform.sh) is helpful during testing. This script is exactly like the
`apply_terraform.sh` except it destroys resources defined in the target state file

`apply_terraform.sh` and `destroy_terraform.sh` take two arguments:

1. The Global variables file
2. The directory that contains the main.tf and *.tfvars variables file of the configuration to apply
1. The directory that contains the main.tf and *.tfvars variables file of the configuration to apply

For example, from the root of this repository, you could apply Tier 0 with a command like:

```bash
scripts/apply_terraform.sh \
src/scripts/apply_terraform.sh \
src/core/globals.tfvars \
src/core/tier-0
```

To apply Tier 1, you could then change the target directory:

```bash
scripts/apply_terraform.sh \
src/scripts/apply_terraform.sh \
src/core/globals.tfvars \
src/core/tier-1
```

Repeating this same pattern, for whatever configuration you wanted to apply and reuse in some automated pipeline.

Use `init_terraform.sh` at [scripts/init_terraform.sh](scripts/init_terraform.sh) to perform just an initialization of the Terraform environment
Use `init_terraform.sh` at [src/scripts/init_terraform.sh](src/scripts/init_terraform.sh) to perform just an initialization of the Terraform environment

To initialize Terraform for Tier 1, you could then change the target directory:

```bash
scripts/init_terraform.sh \
src/scripts/init_terraform.sh \
src/core/tier-1
```

### Terraform Providers

The development container definition downloads the required Terraform plugin providers during the container build so that the container can be transported to an air-gapped network for use. The container also sets the `TF_PLUGIN_CACHE_DIR` environment variable, which Terraform uses as the search location for locally installed providers. If you are not using the container to deploy or if the `TF_PLUGIN_CACHE_DIR` environment variable is not set, Terraform will automatically attempt to download the provider from the internet when you execute the `terraform init` command.

See the development container [README](.devcontainer/README.md) for more details on building and running the container.

## Helpful Links

For more endpoint mappings between AzureCloud and AzureUsGovernment: <https://docs.microsoft.com/en-us/azure/azure-government/compare-azure-government-global-azure#guidance-for-developers/>
Expand Down
66 changes: 51 additions & 15 deletions build/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,62 @@ See the root [README's "Configure the Terraform Backend"](../README.md#Configure

Today, the global.tfvars file and the .tfvars for saca-hub, tier0-2, are well known and stored elsewhere. Reach out to the team if you need them.

Then, to apply and destroy pass those six arguments to the relevant script:
Then, to apply and destroy pass those files as arguments to the relevant script.

There's an [optional argument to display terraform output](#Optionally-display-Terraform-output).

```shell
usage() {
echo "apply_tf.sh: Automation that calls apply terraform given a MLZ configuration and some tfvars"
error_log "usage: apply_tf.sh <mlz config> <globals.tfvars> <saca.tfvars> <tier0.tfvars> <tier1.tfvars> <tier2.tfvars> <display terraform output (y/n)>"
}
```

```shell
# applies terraform in the repo
# assuming src/scripts/mlz_tf_setup.sh has been run before...
./apply_tf.sh \
../src/core/mlz_tf_cfg.var \
./path_to_vars/globals.tfvars \
./path_to_vars/saca-hub.tfvars \
./path_to_vars/tier-0.tfvars \
./path_to_vars/tier-1.tfvars \
./path_to_vars/tier-2.tfvars
./path-to/mlz_tf_cfg.var \
./path-to/globals.tfvars \
./path-to/saca-hub.tfvars \
./path-to/tier-0.tfvars \
./path-to/tier-1.tfvars \
./path-to/tier-2.tfvars \
y
```

```shell
# destroys terraform in the repo
# assuming src/scripts/mlz_tf_setup.sh has been run before...
./destroy_tf.sh \
../src/core/mlz_tf_cfg.var \
./path_to_vars/globals.tfvars \
./path_to_vars/saca-hub.tfvars \
./path_to_vars/tier-0.tfvars \
./path_to_vars/tier-1.tfvars \
./path_to_vars/tier-2.tfvars
./path-to/mlz_tf_cfg.var \
./path-to/globals.tfvars \
./path-to/saca-hub.tfvars \
./path-to/tier-0.tfvars \
./path-to/tier-1.tfvars \
./path-to/tier-2.tfvars \
y
```

### Optionally display Terraform output

There's an optional argument at the end to specify whether or not to display terraform's output. Set it to 'y' if you want to see things as they happen.

By default, if you do not set this argument, terraform output will be sent to /dev/null (to support clean logs in a CI/CD environment) and your logs will look like:

```plaintext
Applying saca-hub (1/5)...
Finished applying saca-hub!
Applying tier-0 (1/5)...
Finished applying tier-0!
Applying tier-1 (1/5)...
Finished applying tier-1!
Applying tier-2 (1/5)...
Finished applying tier-2!
```

## Gotchas

There's wonky behavior with how Log Analytics Workspaces and Azure Monitor diagnostic log settings are deleted at the Azure Resource Manager level.

For example, if you deployed your environment with Terraform, then deleted it with Azure CLI or the Portal, you can end up with orphan/ghost resources that will be deleted at some other unknown time.

To ensure you're able to deploy on-top of existing resources over and over again, __use Terraform to apply and destroy your environment.__
Loading

0 comments on commit b8114aa

Please sign in to comment.