GCP terraform-google-project-factory multiple projects update the service account with new bindings? - google-cloud-platform

I am using the terraform-google-project-factory module to create multiple GCP projects at once. The projects create just fine and I am using the included option to disable the default GCP compute service account and stand-up a new Service Account in each project.
The module has an "sa_role" input where I assign "roles/compute.admin" to the new S.A. However, I would also like to assign some additional IAM roles to that Service Account in the same deployment. The sa_role input seems to only take one string value:
module "project-factory" {
source = "terraform-google-modules/project-factory/google"
version = "12.0.0"
for_each = toset(local.project_names)
random_project_id = true
name = each.key
org_id = local.organization_id
billing_account = local.billing_account
folder_id = google_folder.DQS.id
default_service_account = "disable"
default_network_tier = "PREMIUM"
create_project_sa = true
auto_create_network = false
project_sa_name = local.service_account
sa_role = ["roles/compute.admin"]
activate_apis = ["compute.googleapis.com","storage.googleapis.com","oslogin.googleapis.com",]
}
The output for the Service Account email looks like this:
output "service_account_email" {
value = values(module.project-factory)[*].service_account_email
description = "The email of the default service account"
}
How can I add additional IAM roles to this Service Account in the same main.tf ? This Stack article comes close to what I wish to achieve:
Want to assign multiple Google cloud IAM roles against a service account via terraform
However, I do not know how to reference my Service Account email addresses from the outputs.tf to make them available to the members = part of the data google_iam_policy. My question is, how to get this to work with the data google_iam_policy, or is there another better way to do this?

Related

Set cognito identity pool providers role resolution via Terraform

im trying to deploy cognito for opensearch via terraform. I have a manually built cognito working and ow trying to port it to terraform.
does anyone know how to set the below part?:
Choose role from token
role resolution 'DENY'
Terraform for the identity pool:
resource "aws_cognito_identity_pool" "cognito-identity-pool" {
identity_pool_name = "opensearch-${var.domain_name}-identity-pool"
allow_unauthenticated_identities = false
cognito_identity_providers {
client_id = aws_cognito_user_pool_client.cognito-user-pool-client.id
provider_name = aws_cognito_user_pool.cognito-user-pool.endpoint
}
}
ive tried adding server_side_token_check = false but no joy..
You need to use a different resource, namely aws_cognito_identity_pool_roles_attachment [1]. In order to achieve the same thing you see in the AWS console, you need to add the following block:
resource "aws_cognito_identity_pool_roles_attachment" "name" {
identity_pool_id = aws_cognito_identity_pool.cognito-identity-pool.id
roles = {
"authenticated" = <your-role-arn>
}
role_mapping {
ambiguous_role_resolution = "Deny"
type = "Token"
identity_provider = "${aws_cognito_user_pool.cognito-user-pool.endpoint}:${aws_cognito_user_pool_client.cognito-user-pool-client.id}"
}
}
Note that the roles block is required and the key can be authenticated or unathenticated. Additionally, you will probably have to figure out what kind of permissions the role will need and create it. The example in the documentation can be used as a blueprint. There are also other settings like mapping_rule block which might be of use to you, but since the details are lacking I omitted it from the answer.
[1] https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cognito_identity_pool_roles_attachment

How to add api-key to google secret-manager

With Terraform GCP provider 4.30.0, I can now create an google maps api key and restrict it.
resource "google_apikeys_key" "maps-api-key" {
provider = google-beta
name = "maps-api-key"
display_name = "google-maps-api-key"
project = local.project_id
restrictions {
api_targets {
service = "static-maps-backend.googleapis.com"
}
api_targets {
service = "maps-backend.googleapis.com"
}
api_targets {
service = "places-backend.googleapis.com"
}
browser_key_restrictions {
allowed_referrers = [
"https://${local.project_id}.ey.r.appspot.com/*", # raw url to the app engine service
"*.example.com/*" # Custom DNS name to access to the app
]
}
}
}
The key is created and appears in the console as expected and I can see the API_KEY value.
When I deploy my app, I want it to read the API_KEY string.
My node.js app already reads secrets from secret manager, so I want to add it as a secret.
Another approach could be for the node client library to read the API credential directly, instead of using secret-manager, but I haven't found a way to do that.
I can't work out how to read the key string and store it in the secret.
The terraform resource describes the output
key_string - Output only. An encrypted and signed value held by this
key. This field can be accessed only through the GetKeyString method.
I don't know how to call this method in Terraform, to pass the value to a secret version.
This doesn't work.
v1 = { enabled = true, data = resource.google_apikeys_key.maps-api-key.GetKeyString }
Referencing attributes and arguments does not work the way you tried it. You did quote the correct attribute though, but just failed to specify it:
v1 = {
enabled = true,
data = resource.google_apikeys_key.maps-api-key.key_string
}
Make sure to understand how referencing attributes in Terraform works [1].
[1] https://www.terraform.io/language/expressions/references#references-to-resource-attributes

Get AWS account ID by name

I know there are multiple ways to get AWS account name by its ID, but is the opposite possible? Is there a way to programmatically (API, CLI, terraform etc.) get AWS account ID by its name?
Update: Forgot to mention that these accounts exist under organization structure in a specific OU, maybe this could help.
While this is not ideal, I realized that aws organizations list-accounts-for-parent command is the best compromise. It would give me all accounts within given OU, which I can filter by account name.
Given that my solution will ultimately be implemented in terraform I came out with something like this
data "external" "accounts" {
program = ["aws", "organizations", "list-accounts-for-parent", "--parent-id", local.ou, "--query", "Accounts[?Name==`${local.account_name}`] | [0]"]
}
locals {
ou = "ou-12345678"
account_name = "my-cool-account"
account_id = lookup(data.external.tools_accounts.result, "Id", null)
}
it would execute AWS CLI command, return back a map of key/values if account info is found, and lookup function would retrieve the account ID.
I was able to solve with the following:
data "aws_organizations_organization" "main" {}
locals {
account-name = "account1"
account-index = index(data.aws_organizations_organization.main.accounts.*.name, local.account-name)
account-id = data.aws_organizations_organization.main.accounts[local.account-index].id
}
output "account_id" {
value = local.account-id
}

Add new users as members to GCP Cloud Identity Group using Terraform

I have the gcp-organization-admins Cloud Identity User Group to which I want to add a new user user-01#example.com as a Member using Terraform.
Getting error - Error creating GroupMembership: googleapi: got HTTP response code 404.
The requested URL /v1beta1/gcp-organization-admins#example.com/memberships?alt=json was not found on this server.
Can anyone suggest how to resolve this please.
fyi...Just as a test, I was able to create new Cloud Identity user groups and added some test users into it without any problems using Terraform module https://github.com/terraform-google-modules/terraform-google-group
#=====================
# terraform.tfvars
#=====================
org_admin_user = ["user-01#example.com"]
org_admin_group = "gcp-organization-admins#example.com"
#=========================================================
# add-member.tf (adds user to google group as a member)
#=========================================================
resource "google_cloud_identity_group_membership" "user-01" {
for_each = toset(var.org_admin_user)
provider = google-beta
group = var.org_admin_group
preferred_member_key {
id = each.key
}
roles {
name = "MEMBER"
}
}
I ran into this same problem. Turns out that Terraform wants the GCP Group "name" and not the email address.
So the group attribute of the google_cloud_identity_group_membership resource block should look something like "groups/23097432uwhwiyo" and not "gcp-organization-admins#example.com"
You can look up the group "name" with the following gcloud command:
gcloud identity groups describe "gcp-organization-admins#example.com"
This might to be the same issue as documented here: https://github.com/hashicorp/terraform-provider-google/issues/7616
A comment in that bug mentions the following:
I can work around the above issue by switching the order of the two roles in the resource, i.e.
From
roles { name = "MANAGER" }
roles { name = "MEMBER" }
to
roles { name = "MEMBER" }
roles { name = "MANAGER" }
https://github.com/hashicorp/terraform-provider-google/issues/7616#issuecomment-742779169

Configure Postgres application users with Terraform for RDS

Terraform allows you to define Postgres master user and password with the options username and password. But there is no option to set up an application postgres user, how would you do that?
The AWS RDS resource is only used for creating/updating/deleting the RDS resource itself using the AWS APIs.
To create users or databases on the RDS instance itself you'd either want to use another tool (such as psql - the official command line tool or a configuration management tool such as Ansible) or use Terraform's Postgresql provider.
Assuming you've already created your RDS instance you would then connect to the instance as the master user and then create the application user with something like this:
provider "postgresql" {
host = "postgres_server_ip1"
username = "postgres_user"
password = "postgres_password"
}
resource "postgresql_role" "application_role" {
name = "application"
login = true
password = "application-password"
encrypted = true
}
addition to #ydaetskcoR answer, here is the full example for RDS PostgreSQL;
provider "postgresql" {
scheme = "awspostgres"
host = "db.domain.name"
port = "5432"
username = "db_username"
password = "db_password"
superuser = false
}
resource "postgresql_role" "new_db_role" {
name = "new_db_role"
login = true
password = "db_password"
encrypted_password = true
}
resource "postgresql_database" "new_db" {
name = "new_db"
owner = postgresql_role.new_db_role.name
template = "template0"
lc_collate = "C"
connection_limit = -1
allow_connections = true
}
The above two answers requires the host that runs the terraform has direct access to the RDS database, and usually you do not. I propose to code what you need to do in a lambda function (optionally with secrets manager for retrieving the master password):
resource "aws_lambda_function" "terraform_lambda_func" {
filename = "${path.module}/lambda_function/lambda_function.zip"
...
}
and then use the following data source (example) to call the lambda function.
data "aws_lambda_invocation" "create_app_user" {
function_name = aws_lambda_function.terraform_lambda_func.function_name
input = <<-JSON
{
"step": "create_app_user"
}
JSON
depends_on = [aws_lambda_function.terraform_lambda_func]
provider = aws.primary
}
This solution id generic. It can do what a lambda function can do with AWS API can do, which is basically limitless.