iam trying to create multiple iam users and apply a policy but its giving me a error, are you able to help with this one
I created a variable file that lists the users that needs to be created.
The error i am getting is
Error: Missing resource instance key │ │ on multiple-iam-users.tf
line 12, in resource "aws_iam_policy_attachment" "dev-ec2-read": │
12: name = aws_iam_user.users.name │ │ Because
aws_iam_user.users has "count" set, its attributes must be accessed on
specific instances. │ │ For example, to correlate with indices of
a referring resource, use: │ aws_iam_user.users[count.index]
resource "aws_iam_user" "users" {
name = var.developers[count.index]
count = length(var.developers)
}
resource "aws_iam_policy" "dev-read" {
name = "DevUsers"
policy = file("developer-policy.json")
}
resource "aws_iam_policy_attachment" "dev-ec2-read" {
name = aws_iam_user.users.name
policy_arn = aws_iam_policy.dev-read.arn
}
variable "developers" {
type = list(string)
default = ["james","michael","tony"]
}
I have a separate JSON file with just a EC2 read only policy
Thanks
Since aws_iam_user uses count, you have to access individual instances of your aws_iam_user. You can do this with count again:
resource "aws_iam_policy_attachment" "dev-ec2-read" {
count = length(var.developers)
name = aws_iam_user.users[count.index].name
policy_arn = aws_iam_policy.dev-read.arn
}
Related
I want to create a nat_gateway that allocates to all the public_subnet ids and eip nate ids however terraform is erroring
│ aws_subnet.subnets_public is object with 3 attributes
│ │ each.key is "5"
resource "aws_nat_gateway" "natgw" {
for_each = {
for i, v in var.subnets : i => v
if v.type == "public"
}
allocation_id = aws_eip.nat_eip[each.key].id
subnet_id = aws_subnet.subnets_public[each.key].id
depends_on = [aws_internet_gateway.igw]
}
You are filtering a list in for_each. So that some indexes is missing. Then you are trying to call aws_eip.nat_eip with non-existing index then you are getting error.
If you can share your var.subnets and terraform error then we can help you to fix the error(s).
I am not at all versed in organization policy administration, apologies if my question has an obvious answer.
I tried defining a google_access_context_manager_access_policy via terraform,
resource "google_access_context_manager_access_policy" "org-policy" {
parent = data.google_organization.org.name
title = "Parent policy for ACL restrictions"
}
but I get this error :
╷
│ Error: Error creating AccessPolicy: googleapi: Error 409: Policy already exists with parent organizations/<my-org-id>
│
│ with google_access_context_manager_access_policy.org-policy,
│ on policies.tf line 1, in resource "google_access_context_manager_access_policy" "org-policy":
│ 1: resource "google_access_context_manager_access_policy" "org-policy" {
│
╵
When I perform this command : gcloud access-context-manager policies list --organization <my-org-id> I see that there is a default org policy (that has never been explicitely set)
NAME ORGANIZATION SCOPES TITLE ETAG
<redacted-id> <my-org-id> default policy <what-s-even-this>
Can I create another organization policy upon which I could base all further dependent policies? e.g. google_access_context_manager_access_level
If I can't, how do I redefine the default org policy?
P.S : I'm not sure what tags (community) to invoke for this question. Please advise.
Edit :
Terraform & hashicorp/google versions !
Terraform v1.2.9
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = ">=3.85.0"
}
}
}
Think I've found out: you can only have one policy on a particular level
As your organisation already have one by default you can only create new policies on folder/project.
Assuming you have the latest terraform-google-modules version
resource "google_access_context_manager_access_policy" "org-policy" {
parent = data.google_organization.org.name
title = "Parent policy for ACL restrictions"
scopes = ["folders/00000000000000"]
}
I am new to terraform, I am learning modules. I am stuck in a problem.
I have modules folder as root which contain two folders EC2 and IAM and they have terraform code in them. In the same modules folder I have main.tf file which is a module file for calling EC2 instance terraform code.
For your information, EC2 instance folder contain two files one for instance resource and second is for defining variables.
My EC2 file looks like this.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
# Configure the AWS Provider
provider "aws" {
region = "us-east-1"
}
resource "aws_instance" "web" {
count = "${var.ec2_count}"
ami = "${var.ami}"
instance_type = "${var.instance_type}"
tags = {
Name = "App_Instance"
}
}
My Variable.tf file looks like this.
variable "ec2_count" {
type = number
default = 1
}
variable "ami" {
type = string
}
variable "instance_type" {
type = string
}
My main.tf file looks like this.
module "EC2" {
source = "./EC2"
}
Now I want that when I type terraform plan command, it should take input at the command prompt but it is showing me below error.
PS: I don't want to pass the value of the variable in the module.
C:\Users\PC\Desktop\AWS_Modules\Modules>terraform plan
╷
│ Error: Missing required argument
│
│ on main.tf line 1, in module "EC2":
│ 1: module "EC2" {
│
│ The argument "ami" is required, but no definition was found.
╵
╷
│ Error: Missing required argument
│
│ on main.tf line 1, in module "EC2":
│ 1: module "EC2" {
│
│ The argument "instance_type" is required, but no definition was found.
Since you are new to modules, there are a few things to consider:
Where are you defining the variables
How are you passing the values to variables required by the module
Ideally, you would pass the values to variables when calling the module:
module "EC2" {
source = "./EC2"
ami = "ami-gafadfafa"
instance_type = "t3.micro"
}
However, since you said you do not want to do that, then you need to assign the default values to variables on the module level. This can be achieved with:
variable "ec2_count" {
type = number
default = 1
}
variable "ami" {
type = string
default = "ami-gafadfafa"
}
variable "instance_type" {
type = string
default = "t3.micro"
}
This way, if you do not provide values when calling the module, the instance will be created and you are still leaving the option to provide the values if someone else were to use your module.
The third option is to have a separate variables.tf file in the root module (i.e., the same directory where you are calling the module from) and not define the values for variables. It would be basically a copy+paste of the file you have on the module level without defining any default values. Here is an example:
# variables.tf on the root module level
variable "ec2_count" {
type = number
}
variable "ami" {
type = string
}
variable "instance_type" {
type = string
}
However, in this case, it is impossible not to use the input variable names when calling the module. For this to work with providing the values on the CLI, the code would have to change to:
module "EC2" {
source = "./EC2"
ec2_count = var.ec2_count
ami = var.ami
instance_type = var.instance_type
}
There is no other way the module can know how to map the values you want it to consume without actually setting those variables to a certain value when calling the module.
I want terraform to associate my SQS Management Event with my DLQ management event and i want the same thing done with SQS Data Event and DLQ Data Event.I am getting error messages when i run apply on my code below.please I need some help.
.tfvars
sqs_queue_names = ["CloudTrail_SQS_Management_Event", "CloudTrail_SQS_Data_Event"]
dead_queue_names = ["CloudTrail_DLQ_Management_Event", "CloudTrail_DLQ_Data_Event"]
variable.tf
variable "sqs_queue_names"{
description = "The name of different SQS to be created"
type = set(string)
}
variable "dead_queue_names"{
description = "The name of different Dead Queues to be created"
type = set(string)
}
main.tf
resource "aws_sqs_queue" "CloudTrail_SQS"{
for_each = var.sqs_queue_names
name = each.value
redrive_policy = jsonencode({
deadLetterTargetArn = values(aws_sqs_queue.CloudTrail_SQS_DLQ)[*].arn
maxReceiveCount = var.max_receive_count
})
tags = var.default_tags
}
resource "aws_sqs_queue" "CloudTrail_SQS_DLQ"{
for_each = var.dead_queue_names
name = each.value
tags = var.default_tags
}
ERROR MESSAGES:
Error: error creating SQS Queue (CloudTrail_SQS_Management_Event): InvalidParameterValue: Value {"deadLetterTargetArn":["arn:aws:sqs:us-east-1:123456789012:CloudTrail_DLQ_Data_Event","arn:aws:sqs:us-east-1:123456789012:CloudTrail_DLQ_Management_Event"],"maxReceiveCount":10} for parameter RedrivePolicy is invalid. Reason: Invalid value for deadLetterTargetArn.
│ status code: 400, request id: 9663b896-d86f-569e-92e2-e17152c2db26
│
│ with aws_sqs_queue.CloudTrail_SQS["CloudTrail_SQS_Management_Event"],
│ on main.tf line 5, in resource "aws_sqs_queue" "CloudTrail_SQS":
│ 5: resource "aws_sqs_queue" "CloudTrail_SQS"{
│
╵
╷
│ Error: error creating SQS Queue (CloudTrail_SQS_Data_Event): InvalidParameterValue: Value {"deadLetterTargetArn":["arn:aws:sqs:us-east-1:123456789012:CloudTrail_DLQ_Data_Event","arn:aws:sqs:us-east-1:123456789012:CloudTrail_DLQ_Management_Event"],"maxReceiveCount":10} for parameter RedrivePolicy is invalid. Reason: Invalid value for deadLetterTargetArn.
│ status code: 400, request id: 88b8e4c5-1d50-5559-92f8-bd2297fd231f
│
│ with aws_sqs_queue.CloudTrail_SQS["CloudTrail_SQS_Data_Event"],
│ on main.tf line 5, in resource "aws_sqs_queue" "CloudTrail_SQS":
│ 5: resource "aws_sqs_queue" "CloudTrail_SQS"{
The problem here is that you are not associating the dead letter queue with the corresponding SQS queue. values(aws_sqs_queue.CloudTrail_SQS_DLQ)[*].arn - this essentially passes every dead letter queue ARN for each SQS queue, it does not passes to correct ARN only.
In order to overcome this, I suggest creating a module where we can tie together the SQS queue and its DLQ. We can name for now my_sqs:
my_sqs/variables.tf:
variable "sqs_queue_name"{
description = "The name of different SQS to be created"
type = string
}
variable "dead_queue_name"{
description = "The name of different Dead Queues to be created"
type = string
}
variable "max_receive_count" {
type = number
}
my_sqs/main.tf:
resource "aws_sqs_queue" "sqs" {
name = var.sqs_queue_name
redrive_policy = jsonencode({
deadLetterTargetArn = aws_sqs_queue.dlq.arn
maxReceiveCount = var.max_receive_count
})
}
resource "aws_sqs_queue" "dlq" {
name = var.dead_queue_name
}
Now we can use this module like this:
variables.tf:
# Please not, we are tying the SQS and the DQL together here as well.
variable "queue_names" {
default = [
{
sqs_name = "CloudTrail_SQS_Management_Event"
dlq_name = "CloudTrail_DLQ_Management_Event"
},
{
sqs_name = "CloudTrail_SQS_Data_Event"
dlq_name = "CloudTrail_DLQ_Data_Event"
}
]
}
From the main.tf we call the module we created above:
main.tf:
module "my_sqs" {
source = "./my_sqs"
for_each = {
for sqs, dlq in var.queue_names : sqs => dlq
}
sqs_queue_name = each.value.sqs_name
dead_queue_name = each.value.dlq_name
max_receive_count = 4
}
Please note, this example may work with the latest Terraform versions. It may not work with an older version which does not support having a for_each on a module.
I have a Terraform module that I would like to modify. Currently my module creates a service account. I would like to modify it so that someone could pass in an existing service account OR if one is not passed in, then the module creates a service account is it would have originally.
Originally my service account looked like this:
resource "google_service_account" "scheduler" {
account_id = "${var.prefix}-scheduler"
project = var.project
}
I've added the following variable to my variables.tf file:
variable "service_account_email" {
default = null
description = "Existing service account for running ... jobs. If null a new service account will be created."
}
What I originally thought to do was to add some locals
locals {
service_account_count = var.service_account_email == null ? 1 : 0
service_account_email = var.service_account_email == null ? google_service_account.scheduler.email : var.service_account_email
}
Then I could change my service account to look like
resource "google_service_account" "scheduler" {
count = local.service_account_count
account_id = "${var.prefix}-scheduler"
project = var.project
}
And then wherever I would have referenced google_service_account.scheduler.email I can instead reference local.service_account_email .. It doesn't look like I'm able to do this, however, for a few reasons.
I get the following error if I try to use the locals block that mentioned above:
│ Because google_service_account.scheduler has "count" set, its attributes must be accessed on specific instances.
│
│ F`or example, to correlate with indices of a referring resource, use:
│ google_service_account.scheduler[count.index]
╵
If I change it so that I'm using google_service_account.scheduler[count.index].email instead, I get the following error:
│ Because google_service_account.scheduler has "count" set, its attributes must be accessed on specific instances.
│
│ For example, to correlate with indices of a referring resource, use:
│ google_service_account.scheduler[count.index]
╵
Now I'm sort of stuck, because I can't force any resources that would originally have referenced google_service_account.scheduler.email to instead reference the var.service_account_email variable that is being passed in for cases where we would prefer to use an existing service account.
Since you are using count, you have to use [0] to access your resource:
service_account_email = var.service_account_email == null ? google_service_account.scheduler[0].email : var.service_account_email