Terraform modules azure event subscription optional fields - azure

I am trying to work with terraform modules to create event subscription pointing to storage queue as an endpoint to it.
Below is the module
resource "azurerm_eventgrid_event_subscription" "events" {
name = var.name
scope = var.scope
subject_filter = var.subject_filter
storage_queue_endpoint = var.storage_queue_endpoint
}
and terraform is
module "storage_account__event_subscription" {
source = "../modules/event"
name = "testevent"
scope = test
subject_filter = {
subject_begins_with = "/blobServices/default/containers/test/blobs/in"
}
storage_queue_endpoint = {
storage_account_id = test
queue_name = test
}
}
Error message:
: subject_filter {
Blocks of type "subject_filter" are not expected here.
Error: Unsupported block type
on azure.tf line 90, in module "storage_account__event_subscription":
: storage_queue_endpoint {
Blocks of type "storage_queue_endpoint" are not expected here.
How do i parse the optional fields properly in terraform modules ?

In you module:
resource "azurerm_eventgrid_event_subscription" "events" {
name = var.name
scope = var.scope
subject_filter = {
subject_begins_with = var.subject_begins_with
}
storage_queue_endpoint = var.storage_queue_endpoint
}
Formatting is off here so make sure to run terraform fmt to account for my poor formatting. Also add the variable to the variables.tf file.
Your Terraform file:
module "storage_account__event_subscription" {
source = "../modules/event"
name = "testevent"
scope = test
subject_begins_with = "/blobServices/default/containers/test/blobs/in"
storage_queue_endpoint = {
storage_account_id = test
queue_name = test
}
}
You create the full structure in the module and then you assign the variables in the terraform file.
Anything that will have the same, or generally the same, value can have a default value set in the variables.tf as well so that you get smaller chunks in the TF file.

Related

Produce repeating blocks inside a terraform resource

I am fairly new to terraform and trying to create a google_compute_backend_service using terraform and there are multiple backend blocks inside the resource as shown below:
resource "google_compute_backend_service" "app-backend" {
log_config {
enable = "true"
sample_rate = "1"
}
name = "app-backend"
port_name = "http-34070"
project = "my-project"
protocol = "HTTP"
session_affinity = "NONE"
timeout_sec = "30"
backend {
group = "insatnce-group1"
}
backend {
group = "instance-group2"
}
backend {
group = "instance-group3"
}
health_checks = [google_compute_http_health_check.app-http-l7.name]
}
As seen in the code block above the backend block repeats multiple times. I want to make it dynamic so I do not have to write multiple blocks manually.
I tried the following:
Created a variable in the variables.tf file that contains all the instance groups:
variable "groups" {
type = list(object({
name = string
}))
default = [{ name = "instance-group1"},
{ name = "instance-group2"},
{ name = "instance-group3"}]
}
And modified my resource block to this:
resource "google_compute_backend_service" "app-backend" {
log_config {
enable = "true"
sample_rate = "1"
}
name = "app-backend"
port_name = "http-34070"
project = "my-project"
protocol = "HTTP"
session_affinity = "NONE"
timeout_sec = "30"
dynamic "backend" {
for_each = var.groups
iterator = item
group = item.value.name
}
health_checks = [google_compute_http_health_check.app-http-l7.name]
}
However, when I execute terraform plan I get the following error:
Error: Unsupported argument
│
│ on backend_service.tf line 15, in resource "google_compute_backend_service" "app-backend":
│ 15: group = item.value.name
│
│ An argument named "group" is not expected here.
Where am I going wrong? Is there a better way to achieve this?
You can check the dynamic blocks documentation for the syntax. Otherwise, you had the right idea.
dynamic "backend" {
for_each = var.groups
content {
group = backend.value.name
}
}
You can also simplify the variable structure to make this even easier.
variable "groups" {
type = set(string)
default = ["instance-group1", "instance-group2", "instance-group3"]
}
dynamic "backend" {
for_each = var.groups
content {
group = backend.value
}
}

Unable to Create Terraform Resource Group when using modules

I am optimizing my terraform code by using modules. When i create a resource group module it works perfectly well but it creates two resource groups
i.e.
Temp-AppConfiguration-ResGrp
Temp-AppServices-ResGrp
instead it should only create
Temp-AppConfiguration-ResGrp
Code Resourcegroup.tf.
resource "azurerm_resource_group" "resource" {
name = "${var.environment}-${var.name_apptype}-ResGrp"
location = var.location
tags = {
environment = var.environment
}
}
output "resource_group_name" {
value = "${var.environment}-${var.name_apptype}-ResGrp"
}
output "resource_group_location" {
value = var.location
}
Variable.tf
variable "name_apptype" {
type = string
default = "AppServices"
}
variable "environment" {
type = string
default = "Temp"
}
variable "location" {
type = string
default = "eastus"
}
Main.tf
module "resourcegroup" {
source = "../Modules"
name_apptype = "AppConfiguration"
}
I want to pass name_apptype in main.tf when calling resource group module. So that i don't need to update variable.tf every time.
Any suggestions
where i am doing wrong. Plus i am also unable to output the value, i need it so that i could pass resource group name in the next module i want to create.
Thanks
You need to do that in the Main.tf
module "resourcegroup" {
source = "../Modules"
name_apptype = "AppConfiguration"
}
module "resourcegroup-appservices" {
source = "../Modules"
name_apptype = "AppServices"
}
These create a 2 resources groups with the values that you need, additionally you can remove the default value from the name_apptype variable.
If you want to create with the same module both resource groups you need to use count to iterate over an array of names

Getting Outputs of Resources from Reused Modules

I have the following code that will call a module and make target groups for me based off of the information I pass it in the locals variables. This works just fine, my issue being trying to get the arn of each target group it creates in an output.
locals {
targetgroups_beta = {
targetgroup1 = {
name = "example",
path = "/",
environment = "Beta"
}
targetgroup2 = {
name = "example2",
path = "/",
environment = "Beta"
}
}
}
module "target-groups"{
for_each = local.targetgroups_beta
source = ".//modules/targetgroups"
name-beta = each.value.name
path-beta = each.value.path
environment-beta = each.value.environment
vpc-id = "${module.vpc.vpc_id}"
}
The resource name in the module it is calling is target-group, so based off of what I read I should be able to refer to it by something like this:
output{
value = "${aws_lb_target_group.target-group[0].arn}"
}
When I try this I receive the following when running a terraform plan:
"Because aws_lb_target_group.target-group does not have "count" or "for_each" set, references to it must not include an index key. Remove the bracketed index to refer to the single instance of this resource."
My understanding of this is the module that the for_each is calling isn't running a for_each, so I cannot reference the resources in this way. If I do ""${aws_lb_target_group.target-group.arn}" that reference works technically, but includes the arn for every target group and I plan on adding a lot more. Is there a way to take each of these arns out of this list as its own output?
Code in the module that it is calling for reference:
resource "aws_lb_target_group" "target-group" {
name = "example-${var.name-beta}"
port = 80
protocol = "HTTP"
vpc_id = var.vpc-id
deregistration_delay = 5
tags = {
Environment = "${var.environment-beta}"
}
health_check{
healthy_threshold = 2
unhealthy_threshold = 2
timeout = 10
interval = 15
path = var.path-beta
}
}
If I correctly understand, you are using for_each in your target-groups module. If so to get the outputs, you would have to use something like the following in your main.tf file:
module.target-groups[*].arn
The for_each will create multiple modules, not multiple resources in a single module.
Here is good info on using for_each and count with modules in terraform 0.13.
Update for one module
If you want to use only one module, you can do the following:
module "target-groups"{
target_groups_to_create = local.targetgroups_beta
source = ".//modules/targetgroups"
name-beta = each.value.name
path-beta = each.value.path
environment-beta = each.value.environment
vpc-id = "${module.vpc.vpc_id}"
}
Then in the module:
variable "target_groups_to_create" {}
resource "aws_lb_target_group" "target-group" {
for_each = var.target_groups_to_create
name = "example-${each.value.name}"
port = 80
protocol = "HTTP"
vpc_id = var.vpc-id
deregistration_delay = 5
tags = {
Environment = "${each.value.environment}"
}
health_check{
healthy_threshold = 2
unhealthy_threshold = 2
timeout = 10
interval = 15
path = each.value.path
}
}

Passing Terraform variables between Modules within Modules?

I have an issue I have gotten stuck on building a module from the Kubespray project.
I have the following folder structure:
terraform/
modules/
kubespray/
modules/
compute/
ips/
network/
main.tf
variables.tf
roles/
kubespray/
terraform_setup/
main.tf
Within the kubespray/main.tf module there are variables being passed around:
module "ips" {
source = "./modules/ips"
number_of_k8s_masters = "${var.number_of_k8s_masters}"
number_of_k8s_masters_no_etcd = "${var.number_of_k8s_masters_no_etcd}"
number_of_k8s_nodes = "${var.number_of_k8s_nodes}"
floatingip_pool = "${var.floatingip_pool}"
number_of_bastions = "${var.number_of_bastions}"
external_net = "${var.external_net}"
network_name = "${var.network_name}"
router_id = "${module.network.router_id}"
}
module "compute" {
source = "./modules/compute"
...
k8s_master_fips = "${module.ips.k8s_master_fips}"
k8s_master_no_etcd_fips = "${module.ips.k8s_master_no_etcd_fips}"
k8s_node_fips = "${module.ips.k8s_node_fips}"
bastion_fips = "${module.ips.bastion_fips}"
...
}
output "private_subnet_id" {
value = "${module.network.subnet_id}"
}
output "floating_network_id" {
value = "${var.external_net}"
}
output "router_id" {
value = "${module.network.router_id}"
}
output "k8s_master_fips" {
value = "${concat(module.ips.k8s_master_fips, module.ips.k8s_master_no_etcd_fips)}"
}
output "k8s_node_fips" {
value = "${module.ips.k8s_node_fips}"
}
output "bastion_fips" {
value = "${module.ips.bastion_fips}"
}
If I run my terraform init/apply from inside the submodule in modules/kubespray/ It works fine
If I run from my role/kubespray that has
module "kubespray" {
source = "../../../modules/kubespray"
providers = {
openstack.src = openstack
}
}
it fails with
Error: Invalid index
on ../../../modules/kubespray/modules/compute/main.tf line 670, in
resource "openstack_compute_floatingip_associate_v2" "k8s_node": 670:
floating_ip = "${var.k8s_node_fips[count.index]}"
|----------------
| count.index is 0
| var.k8s_node_fips is empty list of dynamic
Help would be extremely appreicated
When you run terraform, the directory becomes an implicit "root module". As it's explained in the Input Variables documentation, root modules can access TF inputs from the CLI or environment, but all other called modules need to have these passed in.
I'm assuming what's happening could be that your ~kubespray/variables.tf input variables have default values assigned which are taking effect. If that's the case, then these should be copied over to ~terraform_setup/variables.tf and passed into the calling module:
module "kubespray" {
source = "../../../modules/kubespray"
providers = {
openstack.src = openstack
}
}
Without asking for further information, my guess here is that you have an empty array being passed in (${var.k8s_node_fips[count.index]}) with a value greater than 0 for var.node_root_volume_size_in_gb and var.number_of_k8s_nodes_no_floating_ip, causing an error when trying to index an empty array for a string that the resource declaration requires.
Error: Invalid index
on ../../../modules/kubespray/modules/compute/main.tf line 670, in resource "openstack_compute_floatingip_associate_v2" "k8s_node": 670: floating_ip = "${var.k8s_node_fips[count.index]}" |---------------- | count.index is 0 | var.k8s_node_fips is empty list of dynamic
According to the error, it seems this may be a interpolation error at ${var.k8s_node_fips[count.index]}.
What it seems like is happening is that the variable is empty, but the count is set to a non-zero number, thereby causing TF to error out.
Looking at the original project code, seems like the count variable depends on var.node_root_volume_size_in_gb to be 0 to be set to 0; otherwise will set the count to the value of var.number_of_k8s_nodes_no_floating_ip.
Snippet that the error seems to be occuring at:
resource "openstack_compute_instance_v2" "k8s_node_no_floating_ip_custom_volume_size" {
name = "${var.cluster_name}-k8s-node-nf-${count.index+1}"
count = "${var.node_root_volume_size_in_gb > 0 ? var.number_of_k8s_nodes_no_floating_ip : 0}"
availability_zone = "${element(var.az_list, count.index)}"
image_name = "${var.image}"
flavor_id = "${var.flavor_k8s_node}"
key_pair = "${openstack_compute_keypair_v2.k8s.name}"
block_device {
uuid = "${data.openstack_images_image_v2.vm_image.id}"
source_type = "image"
volume_size = "${var.node_root_volume_size_in_gb}"
boot_index = 0
destination_type = "volume"
delete_on_termination = true
}
Hope this helps

Terraform Resource does not have attribute for variable

Running Terraform 0.11.7 and getting the following error:
module.frontend_cfg.var.web_acl: Resource 'data.terraform_remote_state.waf' does not have attribute 'waf_nonprod_id' for variable 'data.terraform_remote_state.waf.waf_nonprod_id'
Below is the terraform file:
module "frontend_cfg"
{
source = "../../../../modules/s3_fe/developers"
region = "us-east-1"
dev_shortname = "cfg"
web_acl = "${data.terraform_remote_state.waf.waf_nonprod_id}"
}
data "terraform_remote_state" "waf" {
backend = "local"
config = {
name = "../../../global/waf/terraform.tfstate"
}
}
The file which creates the tfstate file referenced above is below. This file has had no issues building.
resource "aws_waf_web_acl" "waf_fe_nonprod"
{
name = "fe_nonprod_waf"
metric_name = "fenonprodwaf"
default_action
{
type = "ALLOW"
}
}
output waf_nonprod_id
{
value = "${aws_waf_web_acl.waf_fe_nonprod.id}"
}
I will spare the full output of the cloudfront file, however, the following covers the text:
resource "aws_cloudfront_distribution" "fe_distribution"
{
web_acl_id = "${var.web_acl}"
}
If I put the ID of the waf ID into the web_acl variable, it works just fine, so I suspect the issue is something to do with the way I am calling data. This appears to match documentation though.
Use path instead of name in terraform_remote_state,
https://www.terraform.io/docs/backends/types/local.html
data "terraform_remote_state" "waf" {
backend = "local"
config = {
path = "../../../global/waf/terraform.tfstate"
}
}
or
data "terraform_remote_state" "waf" {
backend = "local"
config = {
path = "${path.module}/../../../global/waf/terraform.tfstate"
}
}
I tested it with terraform version 0.11.7 and 0.11.14
If you upgrade terraform to version 0.12.x, syntax using remote_state ouput has changed.
So change
web_acl = "${data.terraform_remote_state.waf.waf_nonprod_id}"
to
web_acl = data.terraform_remote_state.waf.outputs.waf_nonprod_id
or
web_acl = "${data.terraform_remote_state.waf.outputs.waf_nonprod_id}"

Resources