Widen scope for prod project factory SA to dev (#1263)
* restrict storage role on outputs bucket for stage SAs * grant prod project factory SA authority over prod and dev org policies * network stages delegated grants on dev to prod pf SA * security grants to prod pf SA on dev * tfdoc * tests
This commit is contained in:
parent
367f4b6670
commit
5fb17cb3ac
|
@ -89,7 +89,7 @@ module "branch-dp-dev-sa" {
|
|||
])
|
||||
}
|
||||
iam_storage_roles = {
|
||||
(var.automation.outputs_bucket) = ["roles/storage.admin"]
|
||||
(var.automation.outputs_bucket) = ["roles/storage.objectAdmin"]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,7 +106,7 @@ module "branch-dp-prod-sa" {
|
|||
])
|
||||
}
|
||||
iam_storage_roles = {
|
||||
(var.automation.outputs_bucket) = ["roles/storage.admin"]
|
||||
(var.automation.outputs_bucket) = ["roles/storage.objectAdmin"]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ module "branch-gke-dev-sa" {
|
|||
)
|
||||
}
|
||||
iam_storage_roles = {
|
||||
(var.automation.outputs_bucket) = ["roles/storage.admin"]
|
||||
(var.automation.outputs_bucket) = ["roles/storage.objectAdmin"]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ module "branch-gke-prod-sa" {
|
|||
)
|
||||
}
|
||||
iam_storage_roles = {
|
||||
(var.automation.outputs_bucket) = ["roles/storage.admin"]
|
||||
(var.automation.outputs_bucket) = ["roles/storage.objectAdmin"]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ module "branch-network-sa" {
|
|||
])
|
||||
}
|
||||
iam_storage_roles = {
|
||||
(var.automation.outputs_bucket) = ["roles/storage.admin"]
|
||||
(var.automation.outputs_bucket) = ["roles/storage.objectAdmin"]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ module "branch-pf-dev-sa" {
|
|||
])
|
||||
}
|
||||
iam_storage_roles = {
|
||||
(var.automation.outputs_bucket) = ["roles/storage.admin"]
|
||||
(var.automation.outputs_bucket) = ["roles/storage.objectAdmin"]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ module "branch-pf-prod-sa" {
|
|||
])
|
||||
}
|
||||
iam_storage_roles = {
|
||||
(var.automation.outputs_bucket) = ["roles/storage.admin"]
|
||||
(var.automation.outputs_bucket) = ["roles/storage.objectAdmin"]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,21 +80,32 @@ module "branch-pf-prod-gcs" {
|
|||
}
|
||||
}
|
||||
|
||||
resource "google_organization_iam_member" "org_policy_admin_pf" {
|
||||
for_each = !var.fast_features.project_factory ? {} : {
|
||||
pf-dev = ["teams", "development", module.branch-pf-dev-sa.0.iam_email]
|
||||
pf-prod = ["teams", "production", module.branch-pf-prod-sa.0.iam_email]
|
||||
}
|
||||
resource "google_organization_iam_member" "org_policy_admin_pf_dev" {
|
||||
count = var.fast_features.project_factory ? 1 : 0
|
||||
org_id = var.organization.id
|
||||
role = "roles/orgpolicy.policyAdmin"
|
||||
member = each.value.2
|
||||
member = module.branch-pf-dev-sa.0.iam_email
|
||||
condition {
|
||||
title = "org_policy_tag_pf_scoped"
|
||||
description = "Org policy tag scoped grant for ${each.value.0}/${each.value.1}."
|
||||
title = "org_policy_tag_pf_scoped_dev"
|
||||
description = "Org policy tag scoped grant for project factory dev."
|
||||
expression = <<-END
|
||||
resource.matchTag('${var.organization.id}/${var.tag_names.context}', '${each.value.0}')
|
||||
resource.matchTag('${var.organization.id}/${var.tag_names.context}', 'teams')
|
||||
&&
|
||||
resource.matchTag('${var.organization.id}/${var.tag_names.environment}', '${each.value.1}')
|
||||
resource.matchTag('${var.organization.id}/${var.tag_names.environment}', 'development')
|
||||
END
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_organization_iam_member" "org_policy_admin_pf_prod" {
|
||||
count = var.fast_features.project_factory ? 1 : 0
|
||||
org_id = var.organization.id
|
||||
role = "roles/orgpolicy.policyAdmin"
|
||||
member = module.branch-pf-prod-sa.0.iam_email
|
||||
condition {
|
||||
title = "org_policy_tag_pf_scoped_prod"
|
||||
description = "Org policy tag scoped grant for project factory prod."
|
||||
expression = <<-END
|
||||
resource.matchTag('${var.organization.id}/${var.tag_names.context}', 'teams')
|
||||
END
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,6 @@
|
|||
|
||||
# tfdoc:file:description Sandbox stage resources.
|
||||
|
||||
moved {
|
||||
from = module.branch-sandbox-folder
|
||||
to = module.branch-sandbox-folder.0
|
||||
}
|
||||
|
||||
module "branch-sandbox-folder" {
|
||||
source = "../../../modules/folder"
|
||||
count = var.fast_features.sandbox ? 1 : 0
|
||||
|
@ -43,11 +38,6 @@ module "branch-sandbox-folder" {
|
|||
}
|
||||
}
|
||||
|
||||
moved {
|
||||
from = module.branch-sandbox-gcs
|
||||
to = module.branch-sandbox-gcs.0
|
||||
}
|
||||
|
||||
module "branch-sandbox-gcs" {
|
||||
source = "../../../modules/gcs"
|
||||
count = var.fast_features.sandbox ? 1 : 0
|
||||
|
@ -62,11 +52,6 @@ module "branch-sandbox-gcs" {
|
|||
}
|
||||
}
|
||||
|
||||
moved {
|
||||
from = module.branch-sandbox-sa
|
||||
to = module.branch-sandbox-sa.0
|
||||
}
|
||||
|
||||
module "branch-sandbox-sa" {
|
||||
source = "../../../modules/iam-service-account"
|
||||
count = var.fast_features.sandbox ? 1 : 0
|
||||
|
|
|
@ -60,7 +60,7 @@ module "branch-security-sa" {
|
|||
])
|
||||
}
|
||||
iam_storage_roles = {
|
||||
(var.automation.outputs_bucket) = ["roles/storage.admin"]
|
||||
(var.automation.outputs_bucket) = ["roles/storage.objectAdmin"]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ module "branch-teams-sa" {
|
|||
display_name = "Terraform resman teams service account."
|
||||
prefix = var.prefix
|
||||
iam_storage_roles = {
|
||||
(var.automation.outputs_bucket) = ["roles/storage.admin"]
|
||||
(var.automation.outputs_bucket) = ["roles/storage.objectAdmin"]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ module "dev-spoke-project" {
|
|||
"roles/dns.admin" = compact([
|
||||
try(local.service_accounts.gke-dev, null),
|
||||
try(local.service_accounts.project-factory-dev, null),
|
||||
try(local.service_accounts.project-factory-prod, null),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
@ -97,6 +98,7 @@ resource "google_project_iam_binding" "dev_spoke_project_iam_delegated" {
|
|||
members = compact([
|
||||
try(local.service_accounts.data-platform-dev, null),
|
||||
try(local.service_accounts.project-factory-dev, null),
|
||||
try(local.service_accounts.project-factory-prod, null),
|
||||
try(local.service_accounts.gke-dev, null),
|
||||
])
|
||||
condition {
|
||||
|
|
|
@ -39,6 +39,7 @@ module "dev-spoke-project" {
|
|||
"roles/dns.admin" = compact([
|
||||
try(local.service_accounts.gke-dev, null),
|
||||
try(local.service_accounts.project-factory-dev, null),
|
||||
try(local.service_accounts.project-factory-prod, null),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
@ -97,6 +98,7 @@ resource "google_project_iam_binding" "dev_spoke_project_iam_delegated" {
|
|||
members = compact([
|
||||
try(local.service_accounts.data-platform-dev, null),
|
||||
try(local.service_accounts.project-factory-dev, null),
|
||||
try(local.service_accounts.project-factory-prod, null),
|
||||
try(local.service_accounts.gke-dev, null),
|
||||
])
|
||||
condition {
|
||||
|
|
|
@ -38,6 +38,7 @@ module "dev-spoke-project" {
|
|||
"roles/dns.admin" = compact([
|
||||
try(local.service_accounts.gke-dev, null),
|
||||
try(local.service_accounts.project-factory-dev, null),
|
||||
try(local.service_accounts.project-factory-prod, null),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
@ -122,6 +123,7 @@ resource "google_project_iam_binding" "dev_spoke_project_iam_delegated" {
|
|||
members = compact([
|
||||
try(local.service_accounts.data-platform-dev, null),
|
||||
try(local.service_accounts.project-factory-dev, null),
|
||||
try(local.service_accounts.project-factory-prod, null),
|
||||
try(local.service_accounts.gke-dev, null),
|
||||
])
|
||||
condition {
|
||||
|
|
|
@ -356,9 +356,9 @@ Regions are defined via the `regions` variable which sets up a mapping between t
|
|||
| [outputs_location](variables.tf#L96) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
|
||||
| [psa_ranges](variables.tf#L113) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code title="object({ dev = object({ ranges = map(string) routes = object({ export = bool import = bool }) }) prod = object({ ranges = map(string) routes = object({ export = bool import = bool }) }) })">object({…})</code> | | <code>null</code> | |
|
||||
| [regions](variables.tf#L134) | Region definitions. | <code title="object({ primary = string })">object({…})</code> | | <code title="{ primary = "europe-west1" }">{…}</code> | |
|
||||
| [service_accounts](variables.tf#L144) | Automation service accounts in name => email format. | <code title="object({ data-platform-dev = string data-platform-prod = string project-factory-dev = string project-factory-prod = string })">object({…})</code> | | <code>null</code> | <code>1-resman</code> |
|
||||
| [vpn_onprem_dev_primary_config](variables.tf#L156) | VPN gateway configuration for onprem interconnection from dev in the primary region. | <code title="object({ peer_external_gateways = map(object({ redundancy_type = string interfaces = list(string) })) router_config = object({ create = optional(bool, true) asn = number name = optional(string) keepalive = optional(number) custom_advertise = optional(object({ all_subnets = bool ip_ranges = map(string) })) }) tunnels = map(object({ bgp_peer = object({ address = string asn = number route_priority = optional(number, 1000) custom_advertise = optional(object({ all_subnets = bool all_vpc_subnets = bool all_peer_vpc_subnets = bool ip_ranges = map(string) })) }) bgp_session_range = string ike_version = optional(number, 2) peer_external_gateway_interface = optional(number) peer_gateway = optional(string, "default") router = optional(string) shared_secret = optional(string) vpn_gateway_interface = number })) })">object({…})</code> | | <code>null</code> | |
|
||||
| [vpn_onprem_prod_primary_config](variables.tf#L199) | VPN gateway configuration for onprem interconnection from prod in the primary region. | <code title="object({ peer_external_gateways = map(object({ redundancy_type = string interfaces = list(string) })) router_config = object({ create = optional(bool, true) asn = number name = optional(string) keepalive = optional(number) custom_advertise = optional(object({ all_subnets = bool ip_ranges = map(string) })) }) tunnels = map(object({ bgp_peer = object({ address = string asn = number route_priority = optional(number, 1000) custom_advertise = optional(object({ all_subnets = bool all_vpc_subnets = bool all_peer_vpc_subnets = bool ip_ranges = map(string) })) }) bgp_session_range = string ike_version = optional(number, 2) peer_external_gateway_interface = optional(number) peer_gateway = optional(string, "default") router = optional(string) shared_secret = optional(string) vpn_gateway_interface = number })) })">object({…})</code> | | <code>null</code> | |
|
||||
| [service_accounts](variables.tf#L144) | Automation service accounts in name => email format. | <code title="object({ data-platform-dev = string data-platform-prod = string gke-dev = string gke-prod = string project-factory-dev = string project-factory-prod = string })">object({…})</code> | | <code>null</code> | <code>1-resman</code> |
|
||||
| [vpn_onprem_dev_primary_config](variables.tf#L158) | VPN gateway configuration for onprem interconnection from dev in the primary region. | <code title="object({ peer_external_gateways = map(object({ redundancy_type = string interfaces = list(string) })) router_config = object({ create = optional(bool, true) asn = number name = optional(string) keepalive = optional(number) custom_advertise = optional(object({ all_subnets = bool ip_ranges = map(string) })) }) tunnels = map(object({ bgp_peer = object({ address = string asn = number route_priority = optional(number, 1000) custom_advertise = optional(object({ all_subnets = bool all_vpc_subnets = bool all_peer_vpc_subnets = bool ip_ranges = map(string) })) }) bgp_session_range = string ike_version = optional(number, 2) peer_external_gateway_interface = optional(number) peer_gateway = optional(string, "default") router = optional(string) shared_secret = optional(string) vpn_gateway_interface = number })) })">object({…})</code> | | <code>null</code> | |
|
||||
| [vpn_onprem_prod_primary_config](variables.tf#L201) | VPN gateway configuration for onprem interconnection from prod in the primary region. | <code title="object({ peer_external_gateways = map(object({ redundancy_type = string interfaces = list(string) })) router_config = object({ create = optional(bool, true) asn = number name = optional(string) keepalive = optional(number) custom_advertise = optional(object({ all_subnets = bool ip_ranges = map(string) })) }) tunnels = map(object({ bgp_peer = object({ address = string asn = number route_priority = optional(number, 1000) custom_advertise = optional(object({ all_subnets = bool all_vpc_subnets = bool all_peer_vpc_subnets = bool ip_ranges = map(string) })) }) bgp_session_range = string ike_version = optional(number, 2) peer_external_gateway_interface = optional(number) peer_gateway = optional(string, "default") router = optional(string) shared_secret = optional(string) vpn_gateway_interface = number })) })">object({…})</code> | | <code>null</code> | |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
|
|
@ -37,7 +37,9 @@ module "dev-spoke-project" {
|
|||
}
|
||||
iam = {
|
||||
"roles/dns.admin" = compact([
|
||||
try(local.service_accounts.project-factory-dev, null)
|
||||
try(local.service_accounts.gke-dev, null),
|
||||
try(local.service_accounts.project-factory-dev, null),
|
||||
try(local.service_accounts.project-factory-prod, null),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +97,9 @@ resource "google_project_iam_binding" "dev_spoke_project_iam_delegated" {
|
|||
role = "roles/resourcemanager.projectIamAdmin"
|
||||
members = compact([
|
||||
try(local.service_accounts.data-platform-dev, null),
|
||||
try(local.service_accounts.gke-dev, null),
|
||||
try(local.service_accounts.project-factory-dev, null),
|
||||
try(local.service_accounts.project-factory-prod, null),
|
||||
])
|
||||
condition {
|
||||
title = "dev_stage3_sa_delegated_grants"
|
||||
|
|
|
@ -37,6 +37,7 @@ module "prod-spoke-project" {
|
|||
}
|
||||
iam = {
|
||||
"roles/dns.admin" = compact([
|
||||
try(local.service_accounts.gke-prod, null),
|
||||
try(local.service_accounts.project-factory-prod, null)
|
||||
])
|
||||
}
|
||||
|
@ -95,6 +96,7 @@ resource "google_project_iam_binding" "prod_spoke_project_iam_delegated" {
|
|||
role = "roles/resourcemanager.projectIamAdmin"
|
||||
members = compact([
|
||||
try(local.service_accounts.data-platform-prod, null),
|
||||
try(local.service_accounts.gke-platform-prod, null),
|
||||
try(local.service_accounts.project-factory-prod, null),
|
||||
])
|
||||
condition {
|
||||
|
|
|
@ -147,6 +147,8 @@ variable "service_accounts" {
|
|||
type = object({
|
||||
data-platform-dev = string
|
||||
data-platform-prod = string
|
||||
gke-dev = string
|
||||
gke-prod = string
|
||||
project-factory-dev = string
|
||||
project-factory-prod = string
|
||||
})
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
locals {
|
||||
dev_kms_restricted_admins = [
|
||||
for sa in compact([
|
||||
var.service_accounts.data-platform-dev,
|
||||
var.service_accounts.project-factory-dev,
|
||||
var.service_accounts.data-platform-dev
|
||||
var.service_accounts.project-factory-prod
|
||||
]) : "serviceAccount:${sa}"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
locals {
|
||||
prod_kms_restricted_admins = [
|
||||
for sa in compact([
|
||||
var.service_accounts.project-factory-prod,
|
||||
var.service_accounts.data-platform-prod
|
||||
var.service_accounts.data-platform-prod,
|
||||
var.service_accounts.project-factory-prod
|
||||
]) : "serviceAccount:${sa}"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ folder_ids = {
|
|||
service_accounts = {
|
||||
data-platform-dev = "string"
|
||||
data-platform-prod = "string"
|
||||
gke-dev = "string"
|
||||
gke-prod = "string"
|
||||
project-factory-dev = "string"
|
||||
project-factory-prod = "string"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue