ValidationException error when deploying AWS WorkSpaces via terraform - amazon-web-services

I've been trying to deploy AWS WorkSpaces infrastructure using Terraform. The code itself passes the verify and plan check, but it fails to apply.
Source:
module "networking" {
source = "../../modules/networking"
region = var.region
main_cidr_block = var.main_cidr_block
cidr_block_1 = var.cidr_block_1
cidr_block_2 = var.cidr_block_2
size = var.size
}
resource "aws_directory_service_directory" "main" {
name = var.aws_ds_name
password = var.aws_ds_passwd
size = var.size
type = "SimpleAD"
vpc_settings {
vpc_id = module.networking.main_vpc
subnet_ids = ["${module.networking.private-0}", "${module.networking.private-1}"]
}
}
resource "aws_workspaces_directory" "main" {
directory_id = aws_directory_service_directory.main.id
subnet_ids = ["${module.networking.private-0}", "${module.networking.private-1}"]
}
resource "aws_workspaces_ip_group" "main" {
name = "Contractors."
description = "Main IP access control group"
rules {
source = "10.0.0.0/16"
description = "Contractors"
}
}
Error code:
ValidationException: 2 validation errors detected: Value at 'password' failed to satisfy constraint: Member must satisfy regular expression pattern: (?=^.{8,64}$)((?=.*\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[^A-Za-z0-9\s])(?=.*[a-z])|(?=.*[^A-Za-z0-9\s])(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9\s]))^.*; Value '' at 'name' failed to satisfy constraint: Member must satisfy regular expression pattern: ^([a-zA-Z0-9]+[\\.-])+([a-zA-Z0-9])+$
status code: 400, request id: 073f6e61-775e-4ff9-a88e-e1eab97f8519
on modules/workspaces/workspaces.tf line 10, in resource "aws_directory_service_directory" "main":
10: resource "aws_directory_service_directory" "main" {
I am aware that it is a regex issue with the username/passwords, but I haven't set any users for now, and I've reset the security policies for testing reasons.
Anyone had this issue before?

The AWS API for the directory service enforces a constraint on the password attribute and matches what you are seeing in that error when you run terraform apply:
Password
The password for the directory administrator. The directory creation
process creates a directory administrator account with the user name
Administrator and this password.
If you need to change the password for the administrator account, you
can use the ResetUserPassword API call.
Type: String
Pattern:
(?=^.{8,64}$)((?=.*\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[^A-Za-z0-9\s])(?=.*[a-z])|(?=.*[^A-Za-z0-9\s])(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9\s]))^.*
Required: Yes
Normally Terraform is able to validate this with the plan or validate commands but unfortunately the AWS provider is currently missing an appropriate ValidateFunc so it will only fail at apply time instead at the minute.
If you want this to be caught at plan or validate time then you should raise a feature request for it on the provider issue tracker.

Related

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

gcp google_project_iam_member gives invalid argument message on terraform

I'm using terraform to deploy the following
resource "google_project_iam_custom_role" "brw-user-function-item-registered-role" {
role_id = "brw_user_function_item_registered_role"
title = "brw-user-function-item-registered-role"
description = "Role used by the brw-user-function item-registered"
permissions = [
"storage.objects.create",
"storage.objects.get",
"storage.objects.list"
]
}
resource "google_service_account" "brw-user-function-item-registered-service-account" {
account_id = "brw-user-function-item-reg-svc"
display_name = "brw-user-function-item-registered-service_account"
}
resource "google_project_iam_member" "brw-user-function-item-registered-service-account-binding" {
project = local.project
role = "roles/${google_project_iam_custom_role.brw-user-function-item-registered-role.role_id}"
member = "serviceAccount:${google_service_account.brw-user-function-item-registered-service-account.email}"
depends_on = [
google_project_iam_custom_role.brw-user-function-item-registered-role,
google_service_account.brw-user-function-item-registered-service-account
]
}
However when I try to deploy this through terraform, I get the following error
Request "Create IAM Members roles/brw_user_function_item_registered_role serviceAccount:brw-user-function-item-reg-svc#brw-user.iam.gserviceaccount.com for \"project \\\"BRW-User\\\"\"" returned error: Error retrieving IAM policy for project "BRW-User": googleapi: Error 400: Request contains an invalid argument., badRequest
I'm not sure what is wrong here, I have added the depends_on as well just to make sure it is created in the correct order. Could the member attribute be wrong, I tried giving account_id as well and I still get the same error.
Only predefined roles have the string roles/ in front of the name.
You are using the string:
role = "roles/${google_project_iam_custom_role.brw-user-function-item-registered-role.role_id}"
Change it to:
role = google_project_iam_custom_role.brw-user-function-item-registered-role.name
Note the removal of roles/, changing role_id to name, and removing string interpolation.
In the resource google_project_iam_member, if you are passing a custom role it must be of the format:
[projects|organizations]/{parent-name}/roles/{role-name}
Here is an example:
resource "google_project_iam_member" "access" {
project = var.project_name
role = "projects/${var.project_name}/roles/${google_project_iam_custom_role.customer_access.role_id}"
member = "serviceAccount:${google_service_account.service_account.email}"
}
Also, as a best practice avoid using dashes in the resources name (better underscore) and try not make it too long. I've run into issues with long names.

Terraform "primary workGroup could not be created"

I'm trying to execute query on my table In amazone but i cant execute any query i had this error msg :
Before you run your first query, you need to set up a query result location in Amazon S3.
Your query has the following error(s):
No output location provided. An output location is required either through the Workgroup result configuration setting or as an API input. (Service: AmazonAthena; Status Code: 400; Error Code: InvalidRequestException; Request ID: b6b9aa41-20af-4f4d-91f6-db997e226936)
So i'm trying to add Workgroup but i have this problem
'Error: error creating Athena WorkGroup: InvalidRequestException: primary workGroup could not be created
{
RespMetadata: {
StatusCode: 400,
RequestID: "c20801a0-3c13-48ba-b969-4e28aa5cbf86"
},
AthenaErrorCode: "INVALID_INPUT",
Message_: "primary workGroup could not be created"
}
'
Mycode
resource "aws_s3_bucket" "tony" {
bucket = "tfouh"
}
resource "aws_athena_workgroup" "primary" {
name = "primary"
depends_on = [aws_s3_bucket.tony]
configuration {
enforce_workgroup_configuration = false
publish_cloudwatch_metrics_enabled = true
result_configuration {
output_location = "s3://${aws_s3_bucket.tony.bucket}/"
encryption_configuration {
encryption_option = "SSE_S3"
}
}
}
}
please if there are solution
This probably happens because you already have primary work group. Thus, you can't create new one of the same name. Just create a work group with different name if you want:
name = "primary2"
#Marcin suggested a valid approach, but what may be closer to what you are looking for would to to import existing workgroup into the state:
terraform import aws_athena_workgroup.primary primary
Once the state knows about the already existing resource it can do the plan and apply possible changes.

Terraform throws Error setting IAM policy for service account ... Permission iam.serviceAccounts.setIamPolicy is required

I am trying to create a very simple structure on GCP using Terraform: a compute instance + storage bucket. I did some research across GCP documentation, Terraform documentation, SO questions as well and still can't understand what's the trick here. There is one suggestion to use google_project_iam_binding, but reading thruogh some articles it seems to be dangerous (read: insecure solution). There's also a general answer with only GCP descriptions, nit using tf terms here, which is still a bit confusing. And also concluding the similar question here, I confirm that the domain name ownership was verified via Google Console.
So, I ended up with the following:
data "google_iam_policy" "admin" {
binding {
role = "roles/iam.serviceAccountUser"
members = [
"user:myemail#domain.name",
"serviceAccount:${google_service_account.serviceaccount.email}",
]
}
}
resource "google_service_account" "serviceaccount" {
account_id = "sa-1"
}
resource "google_service_account_iam_policy" "admin-acc-iam" {
service_account_id = google_service_account.serviceaccount.name
policy_data = data.google_iam_policy.admin.policy_data
}
resource "google_storage_bucket_iam_policy" "policy" {
bucket = google_storage_bucket.storage_bucket.name
policy_data = data.google_iam_policy.admin.policy_data
}
resource "google_compute_network" "vpc_network" {
name = "vpc-network"
auto_create_subnetworks = "true"
}
resource "google_compute_instance" "instance_1" {
name = "instance-1"
machine_type = "f1-micro"
boot_disk {
initialize_params {
image = "cos-cloud/cos-stable"
}
}
network_interface {
network = google_compute_network.vpc_network.self_link
access_config {
}
}
}
resource "google_storage_bucket" "storage_bucket" {
name = "bucket-1"
location = "US"
force_destroy = true
website {
main_page_suffix = "index.html"
not_found_page = "404.html"
}
cors {
origin = ["http://the.domain.name"]
method = ["GET", "HEAD", "PUT", "POST", "DELETE"]
response_header = ["*"]
max_age_seconds = 3600
}
}
but if I terraform apply, logs show me an error like that
Error: Error setting IAM policy for service account 'trololo': googleapi: Error 403: Permission iam.serviceAccounts.setIamPolicy is required to perform this operation on service account trololo., forbidden
2020/09/28 19:19:34 [TRACE] statemgr.Filesystem: removing lock metadata file .terraform.tfstate.lock.info
on main.tf line 35, in resource "google_service_account_iam_policy" "admin-acc-iam":
35: resource "google_service_account_iam_policy" "admin-acc-iam" {
2020/09/28 19:19:34 [TRACE] statemgr.Filesystem: unlocking terraform.tfstate using fcntl flock
Error: googleapi: Error 403: The bucket you tried to create is a domain name owned by another user., forbidden
on main.tf line 82, in resource "google_storage_bucket" "storage_bucket":
and some useless debug info. What's wrong? What account is missing what permissions and how to assign them securely?
I found the problem. As always, in 90% of cases, the issue is sitting in front of the computer.
Here are the steps that helped me to understand and to resolve the problem:
I read few more articles and especially this and this answer were very helpful to understand relations between users, service accounts, permissions
I understood that doing terraform destroy is also very important since there is no rollback of unsuccessful deploy of a new infrastructure changes (like with DB migrations for example) - thus you have to clean up either with destroy or manually
completely removed the "user:${var.admin_email}" user account IAM policy since it useless; everything has to be managed by the newly created service account
left the main service account with most permissions untouched (the one which was created manually and downloaded the access key) since Terraform used it's credentials
and changed the IAM policy for the new service account as roles/iam.serviceAccountAdmin instead of a User - thanks #Wojtek_B for the hint
After this everything works smooth!

Terraform: How to pass output from one resource to another?

I'm using Aurora serverless Mysql and ECS and trying to use secrets generated by aws secret manager in a file named rds.tf and want to use it another resource in a file called ecs.tf
resource "random_password" "db_instance_aurora_password" {
length = 40
special = false
keepers = {
database_id = aws_secretsmanager_secret.db_instance_aurora_master_password.id
}
Above is rds.tf, which works and generates a random password. In my second file ecs.tf, I want to use the
resource "aws_ecs_task_definition" "task" {
family = var.service_name
container_definitions = templatefile("${path.module}/templates/task_definition.tpl", {
DB_USERNAME = var.db_username
DB_PASSWORD = random_password.db_instance_aurora_password.result
})
}
How to export, the output of the db_password and use it in another resource(ecs.tf)?
output "aurora_rds_cluster.master_password" {
description = "The master password"
value = random_password.db_instance_aurora_password.result }
If all terraform files are in one directory, you can just reference random_password resource as you do it for the database. Then you might not need to output it.
If it's separated, then you can use terraform modules to achieve what you need. In ECS terraform you can reference RDS module and you will have access to its output:
module "rds" {
source = "path/to/folder/with/rds/terraform"
}
resource "aws_ecs_task_definition" "task" {
family = var.service_name
container_definitions = templatefile("${path.module}/templates/task_definition.tpl", {
DB_USERNAME = var.db_username
DB_PASSWORD = module.rds.aurora_rds_cluster.master_password
})
}
Storing password in terraform's output will store it as a plain text. Even if you use encrypted S3 bucket, password can still be accessed at least by terraform. Another option to share password could be for example by using AWS Parameter Store. Module that creates password can store it in Param Store, and another module that needs a password can read it.
P.S. You might want to add sensitive = true to the password output in order to eliminate password value from logs.