Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 8 additions & 8 deletions .github/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# More Than Certified GitOps MiniCamp 2024

The main purpose of this mini camp is to build a GitOps pipeline to deploy resources, managed by terraform to AWS using GitHub Actions.
The main purpose of this mini camp is to build a GitOps pipeline to deploy resources, managed by terraform, to AWS using GitHub Actions.

[![semantic-release: conventionalcommits](https://img.shields.io/badge/semantic--release-conventionalcommits-blue?logo=semantic-release)](https://github.com/semantic-release/semantic-release) [![GitHub release](https://img.shields.io/github/release/3ware/gitops-2024?include_prereleases=&sort=semver&color=yellow)](https://github.com/3ware/workflows/gitops-2024/) [![issues - workflows](https://img.shields.io/github/issues/3ware/gitops-2024)](https://github.com/3ware/gitops-2024/issues) [![CI](https://img.shields.io/github/actions/workflow/status/3ware/gitops-2024/wait-for-checks.yaml?label=CI&logo=githubactions&logoColor=white)](https://github.com/3ware/workflows/actions/gitops-2024/wait-for-checks.yaml)

Expand Down Expand Up @@ -97,7 +97,7 @@ The main purpose of this mini camp is to build a GitOps pipeline to deploy resou
> Unfortunately this also cannot be automated because action runners, using `GITHUB_TOKEN` for authentication, are unable to run `gh pr ready --undo` as the integration is unavailable. See [open discussion](https://github.com/cli/cli/issues/8910)

- The workflow will run through the tests (fmt, validate, TFLint), then run `terraform plan` and post the plan to the pull request and workflow job summary.
- To approve the plan, comment on the plan with: **terraform plan approved**
- To approve the plan, add the **approved** label.
- When the [Workflows](#workflows) have completed, mark the PR as ready to assign a reviewer from CODEOWNERS. (again cannot be automated on a runner)

### When to apply?
Expand Down Expand Up @@ -198,21 +198,20 @@ This workflow also flags any policy violations defined in [infracost-policy.rego
- Initialise TFLint to download the AWS plugin rules.
- Run `tflint`
- Run [trunk code quality action](https://github.com/marketplace/actions/trunk-check); this runs checkov and trivy security checks.
- Update the PR comments if any of the steps fails and exit the workflow on failure.
- Update the PR comments if any of the steps fail and exit the workflow on failure.

##### Plan

When a draft pull request is opened, and the Test Terraform job has succeeded - a ` terraform plan` will be run.
The workflow uses [TF-via-PR](https://github.com/DevSecTop/TF-via-PR). This action adds a high level plan and detailed drop down style plan to the workflow summary and updates the pull request with a comment.

> [!NOTE]
> Plan will run on `pull_request` events when the test job is successful.
> [!NOTE] > `plan` will run on `pull_request` events when the test job is successful.

##### Apply

After `terraform plan` has been run, assuming the plan is satisfactory, add the 'approved' label to he pull request to approve the plan. The workflow will run again - this time running `terraform apply`, with `plan_parity` set, to ensure the plan has not changed.
After `terraform plan` has been run, assuming the plan is satisfactory, add the 'approved' label to he pull request to approve the plan. The workflow will run again - this time running `terraform apply`. ~~with `plan_parity` set, to ensure the plan has not changed~~

I tried using `pull_request_review` as the apply trigger, but this trigger does not support the paths filter which means an apply could be triggered when adding `.yaml` files - not ideal. See (https://github.com/3ware/gitops-2024/pull/22).
I tried using `pull_request_review` as the apply trigger, but this trigger does not support the paths filter. This means an apply could be triggered when adding non _tf_ files - not ideal. See (https://github.com/3ware/gitops-2024/pull/22).

I also tried using `issue_comment` but, because this runs on the default branch, a diff was always detected between `plan` and `apply` - not ideal. See (https://github.com/3ware/gitops-2024/pull/29)

Expand All @@ -222,7 +221,7 @@ I also tried using `issue_comment` but, because this runs on the default branch,

##### Diff Check

Following a successful apply, another plan is run to check for any diffs. If a diff is detected a pull request comment is added and the workflow exits with a failure. If a diff is not detected, the pull request can be merged.
Following a successful apply, another plan is run to check for any diffs. If a diff is detected, a pull request comment is added and the workflow exits with a failure. If a diff is not detected, the pull request can be merged.

#### Terraform Docs

Expand All @@ -240,3 +239,4 @@ Generate a CHANGELOG and version tag using [semantic release](https://github.com
- [ ] Pull request labels environment
- [ ] Job matrix / branched for multiple environments
- [ ] Replace manual terraform commands with tf-via-pr for fmt and validate now this is supported
- [ ] Raise `plan-parity` issue with TF-via-PR maintainer
6 changes: 3 additions & 3 deletions .github/workflows/terraform-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ jobs:
uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 # v2.9.0
with:
header: status
hide_and_recreate: true
recreate: true
message: |
#### Terraform Plan Approved.

Expand Down Expand Up @@ -268,7 +268,7 @@ jobs:
uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 # v2.9.0
with:
header: status
hide_and_recreate: true
recreate: true
message: |
> [!WARNING]
> ${{ github.workflow }} detected a diff after a successful `terraform apply`
Expand Down Expand Up @@ -309,7 +309,7 @@ jobs:
uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 # v2.9.0
with:
header: status
hide_and_recreate: true
recreate: true
message: |
#### Terraform apply successful :rocket:

Expand Down
20 changes: 20 additions & 0 deletions terraform/development/.terraform.lock.hcl

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

123 changes: 68 additions & 55 deletions terraform/development/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -50,71 +50,84 @@ resource "aws_route_table_association" "gitops_rta" {
route_table_id = aws_route_table.gitops_rt.id
}

# resource "aws_security_group" "grafana_sg" {
# name = "grafana_sg"
# description = "Grafana Server security group"
# vpc_id = aws_vpc.gitops_vpc.id
# }
resource "aws_security_group" "grafana_sg" {
name = "grafana_sg"
description = "Grafana Server security group"
vpc_id = aws_vpc.gitops_vpc.id
}

# resource "aws_vpc_security_group_ingress_rule" "grafana_ingress" {
# description = "Inbound traffic to grafana web interface"
# security_group_id = aws_security_group.grafana_sg.id
# cidr_ipv4 = "0.0.0.0/0"
# from_port = 3000
# ip_protocol = "tcp"
# to_port = 3000
resource "aws_vpc_security_group_ingress_rule" "grafana_ingress" {
description = "Inbound traffic to grafana web interface"
security_group_id = aws_security_group.grafana_sg.id
cidr_ipv4 = "0.0.0.0/0"
from_port = 3000
ip_protocol = "tcp"
to_port = 3000

# tags = {
# Name = "grafana-ingress-sg-rule-${local.environment}"
# }
tags = {
Name = "grafana-ingress-sg-rule-${local.environment}"
}

# lifecycle {
# create_before_destroy = true
# }
# }
lifecycle {
create_before_destroy = true
}
}

# # trunk-ignore(trivy/AVD-AWS-0104): Unrestricted egress traffic permitted for demo purposes
# resource "aws_vpc_security_group_egress_rule" "grafana_egress" {
# description = "Outbound traffic from grafana server"
# security_group_id = aws_security_group.grafana_sg.id
# cidr_ipv4 = "0.0.0.0/0"
# ip_protocol = "-1"
# trunk-ignore(trivy/AVD-AWS-0104): Unrestricted egress traffic permitted for demo purposes
resource "aws_vpc_security_group_egress_rule" "grafana_egress" {
description = "Outbound traffic from grafana server"
security_group_id = aws_security_group.grafana_sg.id
cidr_ipv4 = "0.0.0.0/0"
ip_protocol = "-1"

# tags = {
# Name = "grafana-egress-sg-rule-${local.environment}"
# }
tags = {
Name = "grafana-egress-sg-rule-${local.environment}"
}

# lifecycle {
# create_before_destroy = true
# }
# }
lifecycle {
create_before_destroy = true
}
}

# data "aws_ami" "ubuntu" {
# most_recent = true
data "aws_ami" "ubuntu" {
most_recent = true

# filter {
# name = "name"
# values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
# }
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}

# filter {
# name = "virtualization-type"
# values = ["hvm"]
# }
filter {
name = "virtualization-type"
values = ["hvm"]
}

# owners = ["099720109477"] # Canonical
# }
owners = ["099720109477"] # Canonical
}

# trunk-ignore(trivy/AVD-AWS-0028): IMDS token not required for demo
# trunk-ignore(trivy/AVD-AWS-0131): EBS encryption not required for demo
resource "aws_instance" "grafana_server" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
subnet_id = aws_subnet.gitops_subnet.id
vpc_security_group_ids = [aws_security_group.grafana_sg.id]
user_data = file("userdata.tftpl")

tags = {
Name = "grafana-server-${local.environment}"
}
}

#! Disabling for now because it break the workflow - see PR33
# check "grafana_check" {
# data "http" "grafana" {
# url = "http://${aws_instance.grafana_server.public_ip}:3000"
# method = "HEAD"
# }

# # trunk-ignore(trivy/AVD-AWS-0028): IMDS token not required for demo
# # trunk-ignore(trivy/AVD-AWS-0131): EBS encryption not required for demo
# resource "aws_instance" "grafana_server" {
# ami = data.aws_ami.ubuntu.id
# instance_type = var.instance_type
# subnet_id = aws_subnet.gitops_subnet.id
# vpc_security_group_ids = [aws_security_group.grafana_sg.id]
# user_data = file("userdata.tftpl")

# tags = {
# Name = "grafana-server-${local.environment}"
# assert {
# condition = data.http.grafana.status_code == 200
# error_message = "${data.http.grafana.url} returned an unhealthy status code"
# }
# }
8 changes: 4 additions & 4 deletions terraform/development/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ output "default_tags" {
value = data.aws_default_tags.this.tags
}

# output "grafana_ip" {
# description = "The connection details of the grafana server."
# value = "http://${aws_instance.grafana_server.public_ip}:3000"
# }
output "grafana_ip" {
description = "The connection details of the grafana server."
value = "http://${aws_instance.grafana_server.public_ip}:3000"
}
9 changes: 7 additions & 2 deletions terraform/development/providers.tf
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
terraform {
required_version = ">=1.9.0"
# Must be above 1.9.0 to allow cross-object referencing for input variable validations
required_version = ">=1.9.0, <=2.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.69.0"
version = "~>5.69.0"
}
# http = {
# source = "hashicorp/http"
# version = "~>3.4.5"
# }
}
}

Expand Down
2 changes: 1 addition & 1 deletion terraform/development/terraform.tfvars
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#instance_type = "t2.micro"
instance_type = "t2.micro"
project_id = "gitops-2024"
region = "us-east-1"
subnet_cidr_block = "10.0.2.0/24"
Expand Down
32 changes: 16 additions & 16 deletions terraform/development/variables.tf
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
# locals {
# valid_instance_types = ["t2.micro"]
# }
locals {
valid_instance_types = ["t2.micro"]
}

# variable "instance_type" {
# description = "(Required) Instance type to use. Should be within the free tier"
# type = string
variable "instance_type" {
description = "(Required) Instance type to use. Should be within the free tier"
type = string

# validation {
# condition = contains(local.valid_instance_types, var.instance_type)
# error_message = format(
# "Invalid instance type provided. Received: '%s', Require: '%v'.\n%s",
# var.instance_type,
# join(", ", local.valid_instance_types),
# "Change the instance type variable to one that is permitted."
# )
# }
# }
validation {
condition = contains(local.valid_instance_types, var.instance_type)
error_message = format(
"Invalid instance type provided. Received: '%s', Require: '%v'.\n%s",
var.instance_type,
join(", ", local.valid_instance_types),
"Change the instance type variable to one that is permitted."
)
}
}

variable "project_id" {
description = "(Required) Name of the project"
Expand Down