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
101 changes: 101 additions & 0 deletions modules/aws/github-oidc/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ resource "aws_iam_openid_connect_provider" "github" {
client_id_list = ["sts.amazonaws.com"]
}

data "aws_caller_identity" "current" {}

data "aws_partition" "current" {}

locals {
organizations_account_resources = [
"arn:${data.aws_partition.current.partition}:organizations::${data.aws_caller_identity.current.account_id}:account/*/*",
]

organizations_root_resources = [
"arn:${data.aws_partition.current.partition}:organizations::${data.aws_caller_identity.current.account_id}:root/*/*",
]
}

data "aws_iam_policy_document" "oidc" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
Expand Down Expand Up @@ -130,6 +144,93 @@ data "aws_iam_policy_document" "iam_and_terraform" {
}
}

dynamic "statement" {
for_each = var.enable_organizations_access ? [1] : []

content {
sid = "OrganizationsGlobalRead"
effect = "Allow"
actions = [
"organizations:DescribeOrganization",
"organizations:ListAccounts",
"organizations:ListRoots",
"organizations:ListAWSServiceAccessForOrganization",
]
resources = ["*"]
}
}

dynamic "statement" {
for_each = var.enable_organizations_access ? [1] : []

content {
sid = "OrganizationsManagedAccounts"
effect = "Allow"
actions = [
"organizations:DescribeAccount",
"organizations:ListParents",
"organizations:ListTagsForResource",
"organizations:TagResource",
"organizations:UntagResource",
]
resources = local.organizations_account_resources
}
}

dynamic "statement" {
for_each = var.enable_organizations_access ? [1] : []

content {
sid = "OrganizationsAccountProvisioning"
effect = "Allow"
actions = [
"organizations:CreateAccount",
"organizations:DescribeCreateAccountStatus",
]
resources = ["*"]
}
}

dynamic "statement" {
for_each = var.enable_organizations_access ? [1] : []

content {
sid = "OrganizationsServiceAccess"
effect = "Allow"
actions = [
"organizations:EnableAWSServiceAccess",
"organizations:DisableAWSServiceAccess",
]
resources = ["*"]

condition {
test = "StringEquals"
variable = "organizations:ServicePrincipal"
values = ["sso.amazonaws.com"]
}
}
}

dynamic "statement" {
for_each = var.enable_organizations_access ? [1] : []

content {
sid = "OrganizationsPolicyTypes"
effect = "Allow"
actions = [
"organizations:EnablePolicyType",
"organizations:DisablePolicyType",
]
resources = local.organizations_root_resources

condition {
test = "StringEquals"
variable = "organizations:PolicyType"
values = ["SERVICE_CONTROL_POLICY"]
}
}
}

statement {
sid = "STSandKMS"
effect = "Allow"
Expand Down
6 changes: 6 additions & 0 deletions modules/aws/github-oidc/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ variable "terraform_state_bucket_arn" {
default = null
nullable = true
}

variable "enable_organizations_access" {
description = "Whether to grant AWS Organizations permissions for the root workspace."
type = bool
default = false
}
182 changes: 182 additions & 0 deletions workspaces/root/amplify/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
terraform {
required_version = ">= 1.14.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.65.0"
}
}
}

locals {
apps = {
website = {
name = "DurianPy-Official-Website"
repository = "https://github.com/DurianPy-Davao-Python-User-Group/DurianPy-Official-Website"
platform = "WEB_COMPUTE"
iam_service_role_arn = "arn:aws:iam::682033466856:role/service-role/AmplifySSRLoggingRole-0d6a9bc4-6068-40c6-8023-319670299286"
custom_rules = [
{ source = "https://durianpy.org", target = "https://www.durianpy.org", status = "302" },
{ source = "/<*>", target = "/index.html", status = "404-200" },
]
build_spec = <<-EOT
version: 1
frontend:
phases:
preBuild:
commands:
- npm ci --cache .npm --prefer-offline
build:
commands:
- npm run build
artifacts:
baseDirectory: .next
files:
- '**/*'
cache:
paths:
- .next/cache/**/*
- .npm/**/*
EOT
}
pycon_mini = {
name = "pycon-mini-landing"
repository = "https://github.com/DurianPy-Davao-Python-User-Group/pycon-mini-landing"
platform = "WEB"
custom_rules = [
{ source = "/<*>", target = "/index.html", status = "404-200" },
]
build_spec = <<-EOT
version: 1
frontend:
phases:
preBuild:
commands:
- npm ci --cache .npm --prefer-offline
build:
commands:
- npm run build
artifacts:
baseDirectory: dist
files:
- '**/*'
cache:
paths:
- .npm/**/*
EOT
# environment_variables managed in AWS console; ignored via lifecycle rule
}
}

branches = {
website_main = {
app_key = "website"
branch_name = "main"
stage = "PRODUCTION"
framework = "Next.js - SSR"
}
website_staging = {
app_key = "website"
branch_name = "staging"
enable_basic_auth = true
# basic_auth_credentials managed in AWS console; ignored via lifecycle rule
}
pycon_mini_master = {
app_key = "pycon_mini"
branch_name = "master"
stage = "PRODUCTION"
framework = "Astro"
}
}

domain_associations = {
website_domain = {
app_key = "website"
domain_name = "durianpy.org"
sub_domains = [
{ branch_name = "main", prefix = "" },
{ branch_name = "main", prefix = "www" },
{ branch_name = "staging", prefix = "staging" },
]
}
pycon_mini_domain = {
app_key = "pycon_mini"
domain_name = "durianpy.org"
sub_domains = [
{ branch_name = "master", prefix = "pyconf-mini-2024" },
]
}
}
}

resource "aws_amplify_app" "apps" {
for_each = local.apps

name = each.value.name
repository = each.value.repository
platform = each.value.platform

iam_service_role_arn = try(each.value.iam_service_role_arn, null)

enable_branch_auto_build = try(each.value.enable_branch_auto_build, false)
enable_branch_auto_deletion = try(each.value.enable_branch_auto_deletion, false)
enable_basic_auth = try(each.value.enable_basic_auth, false)

build_spec = try(each.value.build_spec, null)

dynamic "custom_rule" {
for_each = try(each.value.custom_rules, [])
content {
source = custom_rule.value.source
target = custom_rule.value.target
status = custom_rule.value.status
}
}

environment_variables = try(each.value.environment_variables, {})

lifecycle {
# Environment variables may contain secrets managed directly in the AWS console.
ignore_changes = [environment_variables]
}
}

resource "aws_amplify_branch" "branches" {
for_each = local.branches

app_id = aws_amplify_app.apps[each.value.app_key].id
branch_name = each.value.branch_name
stage = try(each.value.stage, null) != "NONE" ? try(each.value.stage, null) : null

display_name = try(each.value.display_name, each.value.branch_name)

enable_auto_build = try(each.value.enable_auto_build, true)
enable_basic_auth = try(each.value.enable_basic_auth, false)
framework = try(each.value.framework, null)

basic_auth_credentials = try(each.value.basic_auth_credentials, null)
environment_variables = try(each.value.environment_variables, {})

lifecycle {
# Credentials are managed directly in the AWS console.
ignore_changes = [basic_auth_credentials]
}
}

resource "aws_amplify_domain_association" "domains" {
for_each = local.domain_associations

app_id = aws_amplify_app.apps[each.value.app_key].id
domain_name = each.value.domain_name

dynamic "sub_domain" {
for_each = each.value.sub_domains
content {
branch_name = sub_domain.value.branch_name
prefix = sub_domain.value.prefix
}
}

# Wait for branches to exist before associating domains
depends_on = [aws_amplify_branch.branches]
}
9 changes: 9 additions & 0 deletions workspaces/root/amplify/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
output "app_ids" {
description = "Map of app key to Amplify app ID."
value = { for k, v in aws_amplify_app.apps : k => v.id }
}

output "app_default_domains" {
description = "Map of app key to default Amplify domain."
value = { for k, v in aws_amplify_app.apps : k => v.default_domain }
}
3 changes: 3 additions & 0 deletions workspaces/root/amplify/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Sensitive attributes (environment_variables, basic_auth_credentials) are
# managed directly in the AWS console and ignored via lifecycle rules.
# No external variables are required.
Loading
Loading