aws generator gh workflow and terraform upload

This commit is contained in:
Noah Masur 2022-09-21 03:49:04 +00:00
parent 720a3cc409
commit f834cc20f4
4 changed files with 341 additions and 1 deletions

View File

@ -118,7 +118,7 @@
# Package servers into images with a generator # Package servers into images with a generator
packages.x86_64-linux = with inputs; { packages.x86_64-linux = with inputs; {
aws = import ./generators/aws.nix { aws = import ./generators/aws {
inherit nixpkgs nixos-generators home-manager globals; inherit nixpkgs nixos-generators home-manager globals;
system = "x86_64-linux"; system = "x86_64-linux";
}; };

80
generators/aws/main.tf Normal file
View File

@ -0,0 +1,80 @@
locals {
image_file = one(fileset(path.root, "result/nixos-amazon-image-*.vhd"))
}
# Upload to S3
resource "aws_s3_object" "image" {
bucket = "your_bucket_name"
key = basename(local.image_file)
source = local.image_file
etag = filemd5(local.image_file)
}
# Setup IAM access for the VM Importer
data "aws_iam_policy_document" "vmimport_trust_policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["vmie.amazonaws.com"]
}
}
}
data "aws_iam_policy_document" "vmimport" {
statement {
actions = [
"s3:GetBucketLocation",
"s3:GetObject",
"s3:ListBucket",
]
resources = [
"arn:aws:s3:::${aws_s3_object.image.bucket}",
"arn:aws:s3:::${aws_s3_object.image.bucket}/*",
]
}
statement {
actions = [
"ec2:ModifySnapshotAttribute",
"ec2:CopySnapshot",
"ec2:RegisterImage",
"ec2:Describe*",
]
resources = ["*"]
}
}
resource "aws_iam_role" "vmimport" {
name = "vmimport"
assume_role_policy = data.aws_iam_policy_document.vmimport_trust_policy.json
inline_policy {
name = "vmimport"
policy = data.aws_iam_policy_document.vmimport.json
}
}
# Import to EBS
resource "aws_ebs_snapshot_import" "image" {
disk_container {
format = "VHD"
user_bucket {
s3_bucket = aws_s3_object.image.bucket
s3_key = aws_s3_object.image.key
}
}
role_name = aws_iam_role.vmimport.name
}
# Convert to AMI
resource "aws_ami" "image" {
description = "Created with NixOS."
name = replace(basename(local.image_file), "/\\.vhd$/", "")
virtualization_type = "hvm"
ebs_block_device {
device_name = "/dev/xvda"
snapshot_id = aws_ebs_snapshot_import.image.id
volume_size = 8
}
}

260
generators/aws/workflow.yml Normal file
View File

@ -0,0 +1,260 @@
name: 'Terraform'
env:
AWS_ACCOUNT_NUMBER: ''
AWS_PLAN_ROLE_NAME: github_actions_plan
AWS_APPLY_ROLE_NAME: github_actions_admin
# Always required. Used for authenticating to AWS, but can also act as your
# default region if you don't want to specify in the provider configuration.
AWS_REGION: us-east-1
# You must change these to fit your project.
TF_VAR_project: change-me
TF_VAR_label: change-me
TF_VAR_owner: Your Name Here
# If storing Terraform in a subdirectory, specify it here.
TERRAFORM_DIRECTORY: .
# Pinned versions of tools to use.
# Check for new releases:
# - https://github.com/hashicorp/terraform/releases
# - https://github.com/fugue/regula/releases
# - https://github.com/terraform-linters/tflint/releases
TERRAFORM_VERSION: 1.2.6
REGULA_VERSION: 2.9.0
TFLINT_VERSION: 0.39.1
# Terraform configuration options
TERRAFORM_PARALLELISM: 10
# These variables are passed to Terraform based on GitHub information.
TF_VAR_repo: ${{ github.repository }}
# This workflow is triggered in the following ways.
on:
# Any push or merge to these branches.
push:
branches:
- dev
- prod
# Any pull request targeting these branches (plan only).
pull_request:
branches:
- dev
- prod
# Any manual trigger on these branches.
workflow_dispatch:
branches:
- dev
- prod
# -------------------------------------------------------------------
# The rest of this workflow can operate without adjustments. Edit the
# below content at your own risk!
# -------------------------------------------------------------------
# Used to connect to AWS IAM
permissions:
id-token: write
contents: read
pull-requests: write
# Only run one workflow at a time for each Terraform state. This prevents
# lockfile conflicts, especially during PR vs push.
concurrency: terraform-${{ github.base_ref || github.ref }}
jobs:
terraform:
name: 'Terraform'
# Change this if you need to run your deployment on-prem.
runs-on: ubuntu-latest
steps:
# Downloads the current repo code to the runner.
- name: Checkout Repo Code
uses: actions/checkout@v2
# Install Nix
- name: Install Nix
uses: cachix/install-nix-action@v17
# Build the image
- name: Build Image
run: nix build .#aws
# Login to AWS
- name: AWS Assume Role
uses: aws-actions/configure-aws-credentials@v1.6.1
with:
role-to-assume: ${{ env.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
# Exports all GitHub Secrets as environment variables prefixed by
# "TF_VAR_", which exposes them to Terraform. The name of each GitHub
# Secret must match its Terraform variable name exactly.
- name: Export Secrets to Terraform Variables
env:
ALL_SECRETS: ${{ toJson(secrets) }}
run: |
echo "$ALL_SECRETS" \
| jq "to_entries | .[] | \"TF_VAR_\" + ( .key | ascii_downcase ) + \"=\" + .value" \
| tr -d \" >> $GITHUB_ENV
# Installs the Terraform binary and some other accessory functions.
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: ${{ env.TERRAFORM_VERSION }}
# Checks whether Terraform is formatted properly. If this fails, you
# should install the pre-commit hook.
- name: Check Formatting
run: |
terraform fmt -no-color -check -diff -recursive
# Downloads a Terraform code lint test.
- uses: terraform-linters/setup-tflint@v1
name: Setup TFLint
with:
tflint_version: v${{ env.TFLINT_VERSION }}
# Sets up linting with this codebase.
- name: Init TFLint
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: tflint --init
# Lints the current code.
- name: Run TFLint
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
tflint -f compact
find ./modules/* -type d -maxdepth 0 | xargs -I __ tflint -f compact --disable-rule=terraform_required_providers --disable-rule=terraform_required_version __
# Connects to remote state backend and download providers.
- name: Terraform Init
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
terraform init \
-backend-config="role_arn=${{ env.AWS_STATE_ROLE_ARN }}" \
-backend-config="region=us-east-1" \
-backend-config="workspace_key_prefix=accounts/${{ env.AWS_ACCOUNT_NUMBER }}/${{ github.repository }}" \
-backend-config="key=state.tfstate" \
-backend-config="dynamodb_table=global-tf-state-lock"
# Set the Terraform Workspace to the current branch name.
- name: Set Terraform Workspace
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
shell: bash
run: |
export WORKSPACE=${{ github.base_ref || github.ref_name }}
terraform workspace select ${WORKSPACE} || terraform workspace new $WORKSPACE
echo "TF_WORKSPACE=$(echo ${WORKSPACE} | sed 's/\//_/g')" >> $GITHUB_ENV
# Checks differences between current code and infrastructure state.
- name: Terraform Plan
id: plan
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
terraform plan \
-input=false \
-no-color \
-out=tfplan \
-parallelism=${TERRAFORM_PARALLELISM} \
-var-file=variables-${TF_WORKSPACE}.tfvars
# Gets the results of the plan for pull requests.
- name: Terraform Show Plan
id: show
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: terraform show -no-color tfplan
# Adds the results of the plan to the pull request.
- name: Comment Plan
uses: actions/github-script@v6
if: github.event_name == 'pull_request'
env:
STDOUT: "```terraform\n${{ steps.show.outputs.stdout }}```"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
// 1. Retrieve existing bot comments for the PR
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
})
const botComment = comments.find(comment => {
return comment.user.type === 'Bot' && comment.body.includes('Terraform Format and Style')
})
// 2. Prepare format of the comment
const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
#### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
#### Terraform Validation 🤖\`${{ steps.validate.outcome }}\`
<details><summary>Validation Output</summary>
\`\`\`\n
${{ steps.validate.outputs.stdout }}
\`\`\`
</details>
#### Terraform Plan 📖\`${{ steps.plan.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\`\n
${process.env.PLAN}
\`\`\`
</details>
*Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Working Directory: \`${{ env.tf_actions_working_dir }}\`, Workflow: \`${{ github.workflow }}\`*`;
// 3. If we have a comment, update it, otherwise create a new one
if (botComment) {
github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: output
})
} else {
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
}
# Downloads Regula and checks whether the plan meets compliance requirements.
- name: Regula Compliance Check
shell: bash
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
REGULA_URL="https://github.com/fugue/regula/releases/download/v${REGULA_VERSION}/regula_${REGULA_VERSION}_Linux_x86_64.tar.gz"
curl -sL "$REGULA_URL" -o regula.tar.gz
tar xzf regula.tar.gz
terraform show -json tfplan | ./regula run
# Deploys infrastructure or changes to infrastructure.
- name: Terraform Apply
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
terraform apply \
-auto-approve \
-input=false \
-parallelism=${TERRAFORM_PARALLELISM} \
tfplan