GCP Failure binding role cloudsql client to a SA through Terraform - google-cloud-platform

When trying to bind cloudsql client role to a SA in GCP with Terraform next error is produce:
Error setting IAM policy for service account '{}': googleapi: Error 400: Role roles/cloudsql.client is not supported for this resource., badRequest
This is my terraform code on file main.tf:
resource "google_service_account" "default" {
account_id = var.service_account_name
display_name = "ECB API Caller Cloud Function SA"
}
data "google_iam_policy" "default" {
binding {
members = ["serviceAccount:${google_service_account.default.email}"]
role = "roles/cloudsql.client"
}
}
resource "google_service_account_iam_policy" "default" {
service_account_id = google_service_account.default.name
policy_data = data.google_iam_policy.default.policy_data
}
Edit: Sorted
After further investigation I found out a way. Not completely sure why, but as far as I am able to understand, you cannot allocate this role to the user but the other way around.
Whatever, this code snippet does the trick:
resource "google_service_account" "default" {
account_id = var.service_account_name
display_name = "ECB API Caller Cloud Function SA"
}
resource "google_project_iam_binding" "project" {
project = var.project_id
role = "roles/cloudsql.client"
members = [
"serviceAccount:${google_service_account.default.email}",
]

Related

How to allow aws programatic user to create resources using assume role

I have created a policy X with ec2 and vpc full access and attached to userA. userA has console access. So, using switch role userA can create instance from console.
Now, userB has programatic access with policy Y with ec2 and vpc full access. But when I tried to create instance using Terraform got error.
Error: creating Security Group (allow-80-22): UnauthorizedOperation: You are not authorized to perform this operation. Encoded authorization failure message:
Even - aws ec2 describe-instances
gives error -
An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.
Anyone can help me on this.
Thanks in advance.
To be honest there are a couple of mistakes in the question itself but I have ignored them and provided a solution to
Create Resources using IAM user with only programmatic access having direct policies attached to it
In general, if you have an AWS IAM user who has programmatic access and already has the required policies attached to it then it's pretty straightforward to create any resources within the permissions. Like any normal use case.
Create Resources using IAM user with only programmatic access with assuming a role that has required policies attached to it(role only)
providers.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
## If you hardcoded the role_arn then it is not required to have two provider configs(one with hardcoded value is enough without any alias).
provider "aws" {
region = "eu-central-1"
}
provider "aws" {
alias = "ec2_and_vpc_full_access"
region = "eu-central-1"
assume_role {
role_arn = data.aws_iam_role.stackoverflow.arn
}
}
resources.tf
/*
!! Important !!
* Currently the AWS secrets(AWS_ACCESS_KEY_ID & AWS_SECRET_ACCESS_KEY) used for authentication to terraform is
* from the user which has direct AWS managed policy [IAMFullAccess] attached to it to read role arn.
*/
# If you have hardcoded role_arn in the provider config this can be ignored and no usage of alias provider config is required
## using default provider to read the role.
data "aws_iam_role" "stackoverflow" {
name = "stackoverflow-ec2-vpc-full-access-role"
}
# Using provider with the role having aws managed policies [ec2 and vpc full access] attached
data "aws_vpc" "default" {
provider = aws.ec2_and_vpc_full_access
default = true
}
# Using provider with the role having AWS managed policies [ec2 and vpc full access] attached
resource "aws_key_pair" "eks_jump_host" {
provider = aws.ec2_and_vpc_full_access
key_name = "ec2keypair"
public_key = file("${path.module}/../../ec2keypair.pub")
}
# Example from https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance
# Using provider with the role having aws managed policies [ec2 and vpc full access] attached
data "aws_ami" "ubuntu" {
provider = aws.ec2_and_vpc_full_access
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}
# Using provider with the role having aws managed policies [ec2 and vpc full access] attached
resource "aws_instance" "terraform-ec2" {
provider = aws.ec2_and_vpc_full_access
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
key_name = "ec2keypair"
security_groups = [aws_security_group.t-allow_tls.name]
}
# Using provider with the role having aws managed policies [ec2 and vpc full access] attached
resource "aws_security_group" "t-allow_tls" {
provider = aws.ec2_and_vpc_full_access
name = "allow-80-22"
description = "Allow TLS inbound traffic"
vpc_id = data.aws_vpc.default.id
ingress {
description = "http"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
}
For a full solution refer to Github Repo, I hope this is something you were looking and helps.

Why does Terraform fail to provision google_cloudfunctions_function_iam_member?

While I am able to provision a single HTTP-triggered GCP cloud function and call it publicly, I can't seem to provision my second function to be called publicly as well.
I have the editor role assigned to my Terraform service account.
google_cloudfunctions_function_iam_member.v2invoker: Creating...
╷
│ Error: Error applying IAM policy for cloudfunctions cloudfunction "projects/myproject/locations/us-central1/functions/v2": Error setting IAM policy for cloudfunctions cloudfunction "projects/myproject/locations/us-central1/functions/v2": googleapi: Error 403: Permission 'cloudfunctions.functions.setIamPolicy' denied on resource 'projects/myproject/locations/us-central1/functions/v2' (or resource may not exist).
│
│ with google_cloudfunctions_function_iam_member.v2invoker,
│ on main.tf line 460, in resource "google_cloudfunctions_function_iam_member" "v2invoker":
│ 460: resource "google_cloudfunctions_function_iam_member" "v2invoker" {
Clarification - the function resource itself is provisioned correctly, only the iam_member resource isn't.
My Terraform config:
resource "google_cloudfunctions_function" "v2" {
name = "v2"
runtime = "nodejs12"
available_memory_mb = 128
source_archive_bucket = google_storage_bucket.functions.name
source_archive_object = google_storage_bucket_object.nodejs_functions.name
entry_point = "v2"
trigger_http = true
}
resource "google_cloudfunctions_function_iam_member" "v2invoker" {
project = google_cloudfunctions_function.v2.project
region = google_cloudfunctions_function.v2.region
cloud_function = google_cloudfunctions_function.v2.name
role = "roles/cloudfunctions.invoker"
member = "allUsers"
}
output "function-v2" {
value = google_cloudfunctions_function.v2.https_trigger_url
}
Added the terraform config for the first (successful) function:
resource "google_cloudfunctions_function" "helloWorld" {
name = "helloWorld"
runtime = "nodejs12"
available_memory_mb = 128
source_archive_bucket = google_storage_bucket.functions.name
source_archive_object = google_storage_bucket_object.nodejs_functions.name
entry_point = "helloWorld"
trigger_http = true
}
# IAM entry so all users can invoke the function
resource "google_cloudfunctions_function_iam_member" "invoker" {
project = google_cloudfunctions_function.helloWorld.project
region = google_cloudfunctions_function.helloWorld.region
cloud_function = google_cloudfunctions_function.helloWorld.name
role = "roles/cloudfunctions.invoker"
member = "allUsers"
}
output "function-helloWorld" {
value = google_cloudfunctions_function.helloWorld.https_trigger_url
}
The current service account used by terraform is one I created with "Editor" role.
According to GCP IAM documentation:
Note: The Editor role contains permissions to create and delete resources for most Google Cloud services. However, it does not contain permissions to perform all actions for all services. See the section above for more information on how to check if a role has the permissions that you need.
Your editor role definitely doesn't have access to perform the cloudfunctions.functions.setIamPolicy action on the cloud function (as the error message mentions).
Assign the Cloud Functions Admin role (roles/cloudfunctions.admin) to the service account.
It should definitely have all permissions required for anything cloud function-related, including but not limited to setting IAM policies on functions.

How can i provision IAM Role in aws with terraform?

as i'm new with terraform, i'd like to ask your help once i got stuck for almost a day.
When trying to apply a IAC to deploy a Nginx service into a ECS(EC2 launch type) on aws i'm facing the following problem:
Error: Error creating IAM Role nginx-iam_role: MalformedPolicyDocument: Has prohibited field Resource status code: 400, request id: 0f1696f4-d86b-4ad1-ba3b-9453f3beff2b
I have already checked the documentation and the syntax is fine. What else could be wrong?
Following the snippet code creating the IAM infra:
provider "aws" {
region = "us-east-2"
}
data "aws_iam_policy_document" "nginx-doc-policy" {
statement {
sid = "1"
actions = [
"ec2:*"
]
resources = ["*"]
}
}
resource "aws_iam_role" "nginx-iam_role" {
name = "nginx-iam_role"
path = "/"
assume_role_policy = "${data.aws_iam_policy_document.nginx-doc-policy.json}"
}
resource "aws_iam_group_policy" "nginx-group-policy" {
name = "my_developer_policy"
group = "${aws_iam_group.nginx-iam-group.name}"
policy = "${data.aws_iam_policy_document.nginx-doc-policy.json}"
}
resource "aws_iam_group" "nginx-iam-group" {
name = "nginx-iam-group"
path = "/"
}
resource "aws_iam_user" "nginx-user" {
name = "nginx-user"
path = "/"
}
resource "aws_iam_user_group_membership" "nginx-membership" {
user = "${aws_iam_user.nginx-user.name}"
groups = ["${aws_iam_group.nginx-iam-group.name}"]
}
If you guys need the remaining code: https://github.com/atilasantos/iac-terraform-nginx.git
You are trying to use the aws_iam_policy_document.nginx-doc-policy policy as an assume_role_policy which does not work as an assume role policy needs to define a principal that you trust and want to grant access to assume the role you are creating.
An assume role policy could look like this is you want to grant access to the role to EC2 instances via instance profiles. At the end you can attach your initial role via a new resource as an inline policy to the role:
data "aws_iam_policy_document" "instance-assume-role-policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
}
}
resource "aws_iam_role" "nginx-iam_role" {
name = "nginx-iam_role"
path = "/"
assume_role_policy = data.aws_iam_policy_document.instance-assume-role-policy.json
}
resource "aws_iam_role_policy" "role_policy" {
name = "role policy"
role = aws_iam_role.nginx-iam_role.id
policy = data.aws_iam_policy_document.nginx-doc-policy.json
}
Instead of attaching the policy as an inline policies you can also create an IAM Policy and attach it to the various iam resources. (e.g.: aws_iam_policy and aws_iam_role_policy_attachment for roles.)
We created a bunch of open-source IAM modules (and others) to make IAM handling easier: Find them here on github. But there are more modules out there that you can try.

Terraform: depends_on for module not working as expected in AWS

I am new to terraform. I was working with terraform v0.12 previously and since I wanted to bring in a dependency between modules, I started using terraform v0.13 recently. I am trying to create an IAM role and attach a few policies to the created role. But the issue arises in policy attachment to the role. Few policies are getting attached to the role but some policies throw an error saying no such role exists while the other policies are attached to the role properly. Is there anything wrong in my implementation?
module.tf
provider "aws" {
region = "ap-southeast-1"
}
#Control Plane role and policies
module "ControlPlane_Role" {
source = "../../templates/IAM/roles"
role_name = var.EKS-master-role
}
module "ControlPlane_Policy1" {
source = "../../templates/IAM/aws_policy"
role_name = var.EKS-master-role
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
depends_on = [module.ControlPlane_Role.role_create]
}
module "ControlPlane_Policy2" {
source = "../../templates/IAM/aws_policy"
role_name = var.EKS-master-role
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSServicePolicy"
depends_on = [module.ControlPlane_Role.role_create]
}
templates/IAM/roles/role.tf
resource "aws_iam_role" "role_create" {
assume_role_policy = data.aws_iam_policy_document.trusted_entity.json
name = var.role_name
}
aws_policy.tf
resource "aws_iam_role_policy_attachment" "aws_policy" {
role = var.role_name
policy_arn = var.policy_arn
}
I'll be passing the variable files separately and there are no issues with that.
Error:
Error: Error attaching policy arn:aws:iam::aws:policy/AmazonEKSClusterPolicy to IAM Role EKS-master: NoSuchEntity: The role with name EKS-master cannot be found.
Error attaching policy arn:aws:iam::aws:policy/AmazonEKSServicePolicy to IAM Role EKS-master: NoSuchEntity: The role with name EKS-master cannot be found.
If I re-run the command terraform apply again on the same resources without any change, the policies are getting attached.
You shouldn't use depends_on except for some exceptional cases. From the templates/IAM/roles, define an output that is the name of the role and in the other modules pass this output (role_name = module.ControlPlane_Role.output_role_name). With this setup, the dependency graph is clear (create the role, create the things that depend on the role) instead of having to manually define dependencies with depends_on.

Terraform policy attachment to a role name in module

I'm trying to create data roles in three environments in AWS using Terraform.
One is an role in root account. This role can is used to login to AWS and can assume data roles in production and staging. This works fine. This is using a separate module.
I have problems when trying to create the roles in prod and staging from a module.
My module looks like this main.tf:
resource "aws_iam_role" "this" {
name = "${var.name}"
description = "${format("%s (managed by Terraform)", var.policy_description)}"
assume_role_policy = "${length(var.custom_principals) == 0 ? data.aws_iam_policy_document.assume_role.json : data.aws_iam_policy_document.assume_role_custom_principals.json}"
}
resource "aws_iam_policy" "this" {
name = "${var.name}"
description = "${format("%s (managed by Terraform)", var.policy_description)}"
policy = "${var.policy}"
}
data "aws_iam_policy_document" "assume_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = ["arn:aws:iam::${var.account_id}:root"]
}
}
}
data "aws_iam_policy_document" "assume_role_custom_principals" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = [
"${var.custom_principals}",
]
}
}
}
resource "aws_iam_role_policy_attachment" "this" {
role = "${aws_iam_role.this.name}"
policy_arn = "${aws_iam_policy.this.arn}"
}
I also have the following in output.tf:
output "role_name" {
value = "${aws_iam_role.this.name}"
}
Next I try to use the module to create two roles in prod and staging.
main.tf:
module "data_role" {
source = "../tf_data_role"
account_id = "${var.account_id}"
name = "data"
policy_description = "Role for data engineers"
custom_principals = [
"arn:aws:iam::${var.master_account_id}:root",
]
policy = "${data.aws_iam_policy_document.data_access.json}"
}
Then I'm trying to attach a AWS policies like this:
resource "aws_iam_role_policy_attachment" "data_readonly_access" {
role = "${module.data_role.role_name}"
policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}
resource "aws_iam_role_policy_attachment" "data_redshift_full_access" {
role = "${module.data_role.role_name}"
policy_arn = "arn:aws:iam::aws:policy/AmazonRedshiftFullAccess"
}
The problem I encounter here is that when I try to run this module the above two policies are not attached in staging but in root account. How can I fix this to make it attach the policies in staging?
I'll assume from your question that staging is its own AWS account, separate from your root account. From the Terraform docs
You can define multiple configurations for the same provider in order to support multiple regions, multiple hosts, etc.
This also applies to creating resources in multiple AWS accounts. To create Terraform resources in two AWS accounts, follow these steps.
In your entrypoint main.tf, define aws providers for the accounts you'll be targeting:
# your normal provider targeting your root account
provider "aws" {
version = "1.40"
region = "us-east-1"
}
provider "aws" {
version = "1.40"
region = "us-east-1"
alias = "staging" # define custom alias
# either use an assumed role or allowed_account_ids to target another account
assume_role {
role_arn = "arn:aws:iam:STAGINGACCOUNTNUMBER:role/Staging"
}
}
(Note: the role arn must exist already and your current AWS credentials must have permission to assume it)
To use them in your module, call your module like this
module "data_role" {
source = "../tf_data_role"
providers = {
aws.staging = "aws.staging"
aws = "aws"
}
account_id = "${var.account_id}"
name = "data"
... remainder of module
}
and define the providers within your module like this
provider "aws" {
alias = "staging"
}
provider "aws" {}
Now when you are declaring resources within your module, you can dictate which AWS provider (and hence which account) to create the resources in, e.g
resource "aws_iam_role" "this" {
provider = "aws.staging" # this aws_iam_role will be created in your staging account
name = "${var.name}"
description = "${format("%s (managed by Terraform)", var.policy_description)}"
assume_role_policy = "${length(var.custom_principals) == 0 ? data.aws_iam_policy_document.assume_role.json : data.aws_iam_policy_document.assume_role_custom_principals.json}"
}
resource "aws_iam_policy" "this" {
# no explicit provider is set here so it will use the "default" (un-aliased) aws provider and create this aws_iam_policy in your root account
name = "${var.name}"
description = "${format("%s (managed by Terraform)", var.policy_description)}"
policy = "${var.policy}"
}