From f834cc20f4ae772d81ede8142d6f67603d6b0952 Mon Sep 17 00:00:00 2001
From: Noah Masur <7386960+nmasur@users.noreply.github.com>
Date: Wed, 21 Sep 2022 03:49:04 +0000
Subject: [PATCH] aws generator gh workflow and terraform upload
---
flake.nix | 2 +-
generators/{aws.nix => aws/default.nix} | 0
generators/aws/main.tf | 80 ++++++++
generators/aws/workflow.yml | 260 ++++++++++++++++++++++++
4 files changed, 341 insertions(+), 1 deletion(-)
rename generators/{aws.nix => aws/default.nix} (100%)
create mode 100644 generators/aws/main.tf
create mode 100644 generators/aws/workflow.yml
diff --git a/flake.nix b/flake.nix
index 4b95a7c..b995b50 100644
--- a/flake.nix
+++ b/flake.nix
@@ -118,7 +118,7 @@
# Package servers into images with a generator
packages.x86_64-linux = with inputs; {
- aws = import ./generators/aws.nix {
+ aws = import ./generators/aws {
inherit nixpkgs nixos-generators home-manager globals;
system = "x86_64-linux";
};
diff --git a/generators/aws.nix b/generators/aws/default.nix
similarity index 100%
rename from generators/aws.nix
rename to generators/aws/default.nix
diff --git a/generators/aws/main.tf b/generators/aws/main.tf
new file mode 100644
index 0000000..4fbb2ca
--- /dev/null
+++ b/generators/aws/main.tf
@@ -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
+ }
+}
diff --git a/generators/aws/workflow.yml b/generators/aws/workflow.yml
new file mode 100644
index 0000000..c0210e2
--- /dev/null
+++ b/generators/aws/workflow.yml
@@ -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 }}\`
+ Validation Output
+
+ \`\`\`\n
+ ${{ steps.validate.outputs.stdout }}
+ \`\`\`
+
+
+
+ #### Terraform Plan 📖\`${{ steps.plan.outcome }}\`
+
+ Show Plan
+
+ \`\`\`\n
+ ${process.env.PLAN}
+ \`\`\`
+
+
+
+ *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