Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for IAM bindings on service accounts to project factory #753

Merged
merged 6 commits into from
Jul 21, 2022
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
44 changes: 26 additions & 18 deletions examples/factories/project-factory/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ The Project Factory is meant to be executed by a Service Account (or a regular u
* `"dns.networks.bindPrivateDNSZone"`
* and role `"roles/orgpolicy.policyAdmin"`
* **on each folder** where projects will be created
* `"roles/logging.admin"`
* `"roles/owner"`
* `"roles/resourcemanager.folderAdmin"`
* `"roles/logging.admin"`
* `"roles/owner"`
* `"roles/resourcemanager.folderAdmin"`
* `"roles/resourcemanager.projectCreator"`
* **on the host project** for the Shared VPC/s
* `"roles/browser"`
* `"roles/browser"`
* `"roles/compute.viewer"`
* `"roles/dns.admin"`
* `"roles/dns.admin"`

## Example

Expand Down Expand Up @@ -176,6 +176,13 @@ service_accounts:
my-service-account:
- roles/compute.admin

# [opt] IAM bindings on the service account resources.
# in name => {role => [members]} format
service_accounts_iam:
another-service-account:
- roles/iam.serviceAccountTokenCreator:
- group: [email protected]
ludoo marked this conversation as resolved.
Show resolved Hide resolved

# [opt] APIs to enable on the project.
services:
- storage.googleapis.com
Expand Down Expand Up @@ -209,7 +216,7 @@ vpc:
subnets_iam:
europe-west1/prod-default-ew1: []
- user:[email protected]
- serviceAccount:service-account1
- serviceAccount:service-account1@my-project.iam.gserviceaccount.com
```
<!-- BEGIN TFDOC -->

Expand All @@ -218,22 +225,23 @@ vpc:
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [billing_account_id](variables.tf#L17) | Billing account id. | <code>string</code> | ✓ | |
| [folder_id](variables.tf#L69) | Folder ID for the folder where the project will be created. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L118) | Project id. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L119) | Project id. | <code>string</code> | ✓ | |
| [billing_alert](variables.tf#L22) | Billing alert configuration. | <code title="object&#40;&#123;&#10; amount &#61; number&#10; thresholds &#61; object&#40;&#123;&#10; current &#61; list&#40;number&#41;&#10; forecasted &#61; list&#40;number&#41;&#10; &#125;&#41;&#10; credit_treatment &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [defaults](variables.tf#L35) | Project factory default values. | <code title="object&#40;&#123;&#10; billing_account_id &#61; string&#10; billing_alert &#61; object&#40;&#123;&#10; amount &#61; number&#10; thresholds &#61; object&#40;&#123;&#10; current &#61; list&#40;number&#41;&#10; forecasted &#61; list&#40;number&#41;&#10; &#125;&#41;&#10; credit_treatment &#61; string&#10; &#125;&#41;&#10; environment_dns_zone &#61; string&#10; essential_contacts &#61; list&#40;string&#41;&#10; labels &#61; map&#40;string&#41;&#10; notification_channels &#61; list&#40;string&#41;&#10; shared_vpc_self_link &#61; string&#10; vpc_host_project &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [dns_zones](variables.tf#L57) | DNS private zones to create as child of var.defaults.environment_dns_zone. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [essential_contacts](variables.tf#L63) | Email contacts to be used for billing and GCP notifications. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [group_iam](variables.tf#L74) | Custom IAM settings in group => [role] format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L80) | Custom IAM settings in role => [principal] format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [kms_service_agents](variables.tf#L86) | KMS IAM configuration in as service => [key]. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [labels](variables.tf#L92) | Labels to be assigned at project level. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [org_policies](variables.tf#L98) | Org-policy overrides at project level. | <code title="object&#40;&#123;&#10; policy_boolean &#61; map&#40;bool&#41;&#10; policy_list &#61; map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; bool&#10; suggested_value &#61; string&#10; status &#61; bool&#10; values &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [prefix](variables.tf#L112) | Prefix used for the project id. | <code>string</code> | | <code>null</code> |
| [service_accounts](variables.tf#L123) | Service accounts to be created, and roles to assign them. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_identities_iam](variables.tf#L136) | Custom IAM settings for service identities in service => [role] format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [services](variables.tf#L129) | Services to be enabled for the project. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [vpc](variables.tf#L143) | VPC configuration for the project. | <code title="object&#40;&#123;&#10; host_project &#61; string&#10; gke_setup &#61; object&#40;&#123;&#10; enable_security_admin &#61; bool&#10; enable_host_service_agent &#61; bool&#10; &#125;&#41;&#10; subnets_iam &#61; map&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [folder_id](variables.tf#L69) | Folder ID for the folder where the project will be created. | <code>string</code> | | <code>null</code> |
| [group_iam](variables.tf#L75) | Custom IAM settings in group => [role] format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L81) | Custom IAM settings in role => [principal] format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [kms_service_agents](variables.tf#L87) | KMS IAM configuration in as service => [key]. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [labels](variables.tf#L93) | Labels to be assigned at project level. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [org_policies](variables.tf#L99) | Org-policy overrides at project level. | <code title="object&#40;&#123;&#10; policy_boolean &#61; map&#40;bool&#41;&#10; policy_list &#61; map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; bool&#10; suggested_value &#61; string&#10; status &#61; bool&#10; values &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [prefix](variables.tf#L113) | Prefix used for the project id. | <code>string</code> | | <code>null</code> |
| [service_accounts](variables.tf#L124) | Service accounts to be created, and roles assigned them on the project. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_accounts_iam](variables.tf#L130) | IAM bindings on service account resources. Format is KEY => {ROLE => [MEMBERS]} | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_identities_iam](variables.tf#L144) | Custom IAM settings for service identities in service => [role] format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [services](variables.tf#L137) | Services to be enabled for the project. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [vpc](variables.tf#L151) | VPC configuration for the project. | <code title="object&#40;&#123;&#10; host_project &#61; string&#10; gke_setup &#61; object&#40;&#123;&#10; enable_security_admin &#61; bool&#10; enable_host_service_agent &#61; bool&#10; &#125;&#41;&#10; subnets_iam &#61; map&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |

## Outputs

Expand Down
1 change: 1 addition & 0 deletions examples/factories/project-factory/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ module "service-accounts" {
for_each = var.service_accounts
name = each.key
project_id = module.project.project_id
iam = lookup(var.service_accounts_iam, each.key, null)
}

resource "google_compute_subnetwork_iam_member" "default" {
Expand Down
10 changes: 9 additions & 1 deletion examples/factories/project-factory/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ variable "essential_contacts" {
variable "folder_id" {
description = "Folder ID for the folder where the project will be created."
type = string
default = null
}

variable "group_iam" {
Expand Down Expand Up @@ -121,11 +122,18 @@ variable "project_id" {
}

variable "service_accounts" {
description = "Service accounts to be created, and roles to assign them."
description = "Service accounts to be created, and roles assigned them on the project."
type = map(list(string))
default = {}
}

variable "service_accounts_iam" {
description = "IAM bindings on service account resources. Format is KEY => {ROLE => [MEMBERS]}"
type = map(map(list(string)))
default = {}
nullable = false
}

variable "services" {
description = "Services to be enabled for the project."
type = list(string)
Expand Down
1 change: 1 addition & 0 deletions fast/assets/schemas/project.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ labels: map(str(), key=str(), required=False)
org_policies: include('org_policies', required=False)
secrets: map(list(str()), key=str(), required=False)
service_accounts: map(list(str()), required=False)
service_accounts_iam: map(map(list(str())), required=False)
services: list(str(matches='^[a-z-]*\.googleapis\.com$'), required=False)
service_identities_iam: map(list(str()), key=str(), required=False)
vpc: include('vpc', required=False)
Expand Down
1 change: 1 addition & 0 deletions fast/stages/03-project-factory/dev/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ module "projects" {
org_policies = try(each.value.org_policies, null)
prefix = var.prefix
service_accounts = try(each.value.service_accounts, {})
service_accounts_iam = try(each.value.service_accounts_iam, {})
services = try(each.value.services, [])
service_identities_iam = try(each.value.service_identities_iam, {})
vpc = try(each.value.vpc, null)
Expand Down
13 changes: 13 additions & 0 deletions tests/examples/factories/project_factory/fixture/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,19 @@ variable "defaults_file" {
default = "./defaults.yaml"
}

variable "service_accounts" {
description = "Service accounts to be created, and roles assigned them on the project."
type = map(list(string))
default = {}
}

variable "service_accounts_iam" {
description = "IAM bindings on service account resources. Format is KEY => {ROLE => [MEMBERS]}"
type = map(map(list(string)))
default = {}
nullable = false
}

variable "shared_vpc_self_link" {
description = "Self link for the shared VPC."
type = string
Expand Down
20 changes: 19 additions & 1 deletion tests/examples/factories/project_factory/test_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,25 @@
# See the License for the specific language governing permissions and
# limitations under the License.

def test_counts(e2e_plan_runner):

def test_plan(e2e_plan_runner):
"Check for a clean plan"
modules, resources = e2e_plan_runner()
assert len(modules) > 0 and len(resources) > 0


def test_plan_service_accounts(e2e_plan_runner):
"Check for a clean plan"
service_accounts = '''{
sa-001 = []
sa-002 = ["roles/owner"]
}'''
service_accounts_iam = '''{
sa-002 = {
"roles/iam.serviceAccountTokenCreator" = ["group:[email protected]"]
}
}'''
modules, resources = e2e_plan_runner(
service_accounts=service_accounts,
service_accounts_iam=service_accounts_iam)
assert len(modules) > 0 and len(resources) > 0