diff --git a/.github/workflows/deployment-aws.yaml b/.github/workflows/deployment-aws.yaml
new file mode 100644
index 0000000..4bacc16
--- /dev/null
+++ b/.github/workflows/deployment-aws.yaml
@@ -0,0 +1,31 @@
+run-name: "☁️ AWS › Deploy"
+name: "☁️ AWS › Deploy"
+
+on: [push]
+
+permissions:
+ id-token: write
+ contents: read
+
+jobs:
+ job-publish:
+ name: "📦 Publish to AWS S3/CloudFront"
+ runs-on: ubuntu-latest
+ steps:
+ - name: "Checkout"
+ id: task_publish_checkout
+ uses: actions/checkout@v4
+
+ - name: "Configure AWS Credentials"
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
+ aws-region: us-east-1
+
+ - name: "Sync to S3"
+ run: |
+ aws s3 sync src/ s3://gregh.dev/ --delete --cache-control "public, max-age=3600"
+
+ - name: "Invalidate CloudFront Cache"
+ run: |
+ aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} --paths "/*"
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..dd73d39
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+.DS_Store
+*.pdf
+*.iso
+*.dmg
+*.tar
+truenas-*/
+.terraform/
+*.tfstate
+*.tfstate.backup
diff --git a/src/index.html b/src/index.html
index efe3904..c0f8c3d 100644
--- a/src/index.html
+++ b/src/index.html
@@ -95,6 +95,7 @@
Feb 2024 - Present
- • Architected and maintained AWS/GCP hybrid cloud infrastructure
+ - • Implemented comprehensive security posture using AWS WAF, GuardDuty, and Security Hub
- • Led automation and incident response initiatives
- • Managed CI/CD pipelines using GitHub Actions
@@ -126,8 +127,8 @@
- • EC2, EKS, Lambda
- • RDS, DynamoDB
- - • API Gateway, WAF
- - • CloudWatch, CloudTrail
+ - • API Gateway, WAF, Shield
+ - • CloudWatch, CloudTrail, GuardDuty
@@ -147,10 +148,10 @@
Infrastructure
- • Kubernetes/EKS
- - • Terraform
- - • Docker
- - • GitHub Actions
- - • Cloudflare WAF
+ - • Terraform/OpenTofu
+ - • Docker/Containerd
+ - • GitHub Actions/GitOps
+ - • Cloudflare WAF/Tunnels
@@ -194,7 +195,7 @@
Kubernetes Platform Engineering
Architected and implemented a EKS-based and EC2 Kubernetes Deployments integrated with GitOps workflows ArgoCD,
- cloudflare waf, and automated scaling policies. Reduced deployment time by 70%.
+ AWS WAF, and automated scaling policies. Reduced deployment time by 70%.
diff --git a/terraform/.terraform.lock.hcl b/terraform/.terraform.lock.hcl
new file mode 100644
index 0000000..cdc1668
--- /dev/null
+++ b/terraform/.terraform.lock.hcl
@@ -0,0 +1,25 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/aws" {
+ version = "5.100.0"
+ constraints = "~> 5.0"
+ hashes = [
+ "h1:Ijt7pOlB7Tr7maGQIqtsLFbl7pSMIj06TVdkoSBcYOw=",
+ "zh:054b8dd49f0549c9a7cc27d159e45327b7b65cf404da5e5a20da154b90b8a644",
+ "zh:0b97bf8d5e03d15d83cc40b0530a1f84b459354939ba6f135a0086c20ebbe6b2",
+ "zh:1589a2266af699cbd5d80737a0fe02e54ec9cf2ca54e7e00ac51c7359056f274",
+ "zh:6330766f1d85f01ae6ea90d1b214b8b74cc8c1badc4696b165b36ddd4cc15f7b",
+ "zh:7c8c2e30d8e55291b86fcb64bdf6c25489d538688545eb48fd74ad622e5d3862",
+ "zh:99b1003bd9bd32ee323544da897148f46a527f622dc3971af63ea3e251596342",
+ "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
+ "zh:9f8b909d3ec50ade83c8062290378b1ec553edef6a447c56dadc01a99f4eaa93",
+ "zh:aaef921ff9aabaf8b1869a86d692ebd24fbd4e12c21205034bb679b9caf883a2",
+ "zh:ac882313207aba00dd5a76dbd572a0ddc818bb9cbf5c9d61b28fe30efaec951e",
+ "zh:bb64e8aff37becab373a1a0cc1080990785304141af42ed6aa3dd4913b000421",
+ "zh:dfe495f6621df5540d9c92ad40b8067376350b005c637ea6efac5dc15028add4",
+ "zh:f0ddf0eaf052766cfe09dea8200a946519f653c384ab4336e2a4a64fdd6310e9",
+ "zh:f1b7e684f4c7ae1eed272b6de7d2049bb87a0275cb04dbb7cda6636f600699c9",
+ "zh:ff461571e3f233699bf690db319dfe46aec75e58726636a0d97dd9ac6e32fb70",
+ ]
+}
diff --git a/terraform/github-role-policy.json b/terraform/github-role-policy.json
new file mode 100644
index 0000000..bd01e58
--- /dev/null
+++ b/terraform/github-role-policy.json
@@ -0,0 +1,27 @@
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": [
+ "s3:PutObject",
+ "s3:GetObject",
+ "s3:DeleteObject",
+ "s3:ListBucket"
+ ],
+ "Resource": [
+ "arn:aws:s3:::gregh.dev",
+ "arn:aws:s3:::gregh.dev/*"
+ ]
+ },
+ {
+ "Effect": "Allow",
+ "Action": [
+ "cloudfront:CreateInvalidation",
+ "cloudfront:GetInvalidation",
+ "cloudfront:ListInvalidations"
+ ],
+ "Resource": "arn:aws:cloudfront::471112517070:distribution/ERJQO1UEJ8IM7"
+ }
+ ]
+}
diff --git a/terraform/github-role-trust-policy.json b/terraform/github-role-trust-policy.json
new file mode 100644
index 0000000..83b256f
--- /dev/null
+++ b/terraform/github-role-trust-policy.json
@@ -0,0 +1,20 @@
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": {
+ "Federated": "arn:aws:iam::471112517070:oidc-provider/token.actions.githubusercontent.com"
+ },
+ "Action": "sts:AssumeRoleWithWebIdentity",
+ "Condition": {
+ "StringEquals": {
+ "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
+ },
+ "StringLike": {
+ "token.actions.githubusercontent.com:sub": "repo:ghndrx/gregh.dev:*"
+ }
+ }
+ }
+ ]
+}
diff --git a/terraform/main.tf b/terraform/main.tf
new file mode 100644
index 0000000..231d0c1
--- /dev/null
+++ b/terraform/main.tf
@@ -0,0 +1,254 @@
+terraform {
+ required_version = ">= 1.0"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = "~> 5.0"
+ }
+ }
+
+ backend "s3" {
+ bucket = "gregh-terraform-state"
+ key = "gregh-dev/terraform.tfstate"
+ region = "us-east-1"
+ encrypt = true
+ dynamodb_table = "gregh-terraform-locks"
+ }
+}
+
+provider "aws" {
+ region = "us-east-1"
+ profile = "production"
+}
+
+# S3 bucket for static website
+resource "aws_s3_bucket" "website" {
+ bucket = "gregh.dev"
+
+ tags = {
+ Name = "gregh.dev"
+ Environment = "production"
+ }
+}
+
+resource "aws_s3_bucket_public_access_block" "website" {
+ bucket = aws_s3_bucket.website.id
+
+ block_public_acls = true
+ block_public_policy = true
+ ignore_public_acls = true
+ restrict_public_buckets = true
+}
+
+resource "aws_s3_bucket_versioning" "website" {
+ bucket = aws_s3_bucket.website.id
+
+ versioning_configuration {
+ status = "Enabled"
+ }
+}
+
+resource "aws_s3_bucket_server_side_encryption_configuration" "website" {
+ bucket = aws_s3_bucket.website.id
+
+ rule {
+ apply_server_side_encryption_by_default {
+ sse_algorithm = "AES256"
+ }
+ }
+}
+
+# CloudFront Origin Access Control
+resource "aws_cloudfront_origin_access_control" "website" {
+ name = "gregh-dev-oac"
+ description = "OAC for gregh.dev"
+ origin_access_control_origin_type = "s3"
+ signing_behavior = "always"
+ signing_protocol = "sigv4"
+}
+
+# S3 bucket policy to allow CloudFront access
+resource "aws_s3_bucket_policy" "website" {
+ bucket = aws_s3_bucket.website.id
+
+ policy = jsonencode({
+ Version = "2012-10-17"
+ Statement = [
+ {
+ Sid = "AllowCloudFrontServicePrincipal"
+ Effect = "Allow"
+ Principal = {
+ Service = "cloudfront.amazonaws.com"
+ }
+ Action = "s3:GetObject"
+ Resource = "${aws_s3_bucket.website.arn}/*"
+ Condition = {
+ StringEquals = {
+ "AWS:SourceArn" = aws_cloudfront_distribution.website.arn
+ }
+ }
+ }
+ ]
+ })
+}
+
+# ACM Certificate for CloudFront (must be in us-east-1)
+resource "aws_acm_certificate" "website" {
+ domain_name = "gregh.dev"
+ subject_alternative_names = ["www.gregh.dev"]
+ validation_method = "DNS"
+
+ lifecycle {
+ create_before_destroy = true
+ }
+
+ tags = {
+ Name = "gregh.dev"
+ }
+}
+
+# CloudFront distribution
+resource "aws_cloudfront_distribution" "website" {
+ enabled = true
+ is_ipv6_enabled = true
+ comment = "gregh.dev static website"
+ default_root_object = "index.html"
+ price_class = "PriceClass_100"
+ aliases = ["gregh.dev", "www.gregh.dev"]
+
+ origin {
+ domain_name = aws_s3_bucket.website.bucket_regional_domain_name
+ origin_id = "S3-gregh.dev"
+ origin_access_control_id = aws_cloudfront_origin_access_control.website.id
+ }
+
+ default_cache_behavior {
+ allowed_methods = ["GET", "HEAD", "OPTIONS"]
+ cached_methods = ["GET", "HEAD"]
+ target_origin_id = "S3-gregh.dev"
+ viewer_protocol_policy = "redirect-to-https"
+ compress = true
+
+ forwarded_values {
+ query_string = false
+ cookies {
+ forward = "none"
+ }
+ }
+
+ min_ttl = 0
+ default_ttl = 3600
+ max_ttl = 86400
+ }
+
+ custom_error_response {
+ error_code = 404
+ response_code = 404
+ response_page_path = "/index.html"
+ }
+
+ custom_error_response {
+ error_code = 403
+ response_code = 404
+ response_page_path = "/index.html"
+ }
+
+ restrictions {
+ geo_restriction {
+ restriction_type = "none"
+ }
+ }
+
+ viewer_certificate {
+ acm_certificate_arn = aws_acm_certificate.website.arn
+ ssl_support_method = "sni-only"
+ minimum_protocol_version = "TLSv1.2_2021"
+ }
+
+ tags = {
+ Name = "gregh.dev"
+ Environment = "production"
+ }
+}
+
+# Route53 Hosted Zone
+resource "aws_route53_zone" "website" {
+ name = "gregh.dev"
+
+ tags = {
+ Name = "gregh.dev"
+ Environment = "production"
+ }
+}
+
+# Route53 records for ACM validation
+resource "aws_route53_record" "cert_validation" {
+ for_each = {
+ for dvo in aws_acm_certificate.website.domain_validation_options : dvo.domain_name => {
+ name = dvo.resource_record_name
+ record = dvo.resource_record_value
+ type = dvo.resource_record_type
+ }
+ }
+
+ allow_overwrite = true
+ name = each.value.name
+ records = [each.value.record]
+ ttl = 60
+ type = each.value.type
+ zone_id = aws_route53_zone.website.zone_id
+}
+
+# ACM certificate validation
+resource "aws_acm_certificate_validation" "website" {
+ certificate_arn = aws_acm_certificate.website.arn
+ validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
+}
+
+# Route53 A record for apex domain
+resource "aws_route53_record" "website_apex" {
+ zone_id = aws_route53_zone.website.zone_id
+ name = "gregh.dev"
+ type = "A"
+
+ alias {
+ name = aws_cloudfront_distribution.website.domain_name
+ zone_id = aws_cloudfront_distribution.website.hosted_zone_id
+ evaluate_target_health = false
+ }
+}
+
+# Route53 A record for www subdomain
+resource "aws_route53_record" "website_www" {
+ zone_id = aws_route53_zone.website.zone_id
+ name = "www.gregh.dev"
+ type = "A"
+
+ alias {
+ name = aws_cloudfront_distribution.website.domain_name
+ zone_id = aws_cloudfront_distribution.website.hosted_zone_id
+ evaluate_target_health = false
+ }
+}
+
+# Outputs
+output "nameservers" {
+ value = aws_route53_zone.website.name_servers
+ description = "Route53 nameservers - update these at your domain registrar"
+}
+
+output "s3_bucket_name" {
+ value = aws_s3_bucket.website.id
+ description = "Name of the S3 bucket"
+}
+
+output "cloudfront_distribution_id" {
+ value = aws_cloudfront_distribution.website.id
+ description = "CloudFront distribution ID"
+}
+
+output "cloudfront_domain_name" {
+ value = aws_cloudfront_distribution.website.domain_name
+ description = "CloudFront distribution domain name"
+}