Terraform public ip output on Azure - azure

I am following the example [1] to output the public IP of a new VM created on Azure with Terraform. It works fine when creating only 1 VM, but when I add a counter (default 2), it doesn't output anything.
This is how I am modifying the .tf file:
variable "count" {
default = "2"
}
...
resource "azurerm_public_ip" "test" {
name = "test-pip"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
public_ip_address_allocation = "Dynamic"
idle_timeout_in_minutes = 30
tags {
environment = "test"
}
}
...
data "azurerm_public_ip" "test" {
count = "${var.count}"
name = "${element(azurerm_public_ip.test.*.name, count.index)}"
resource_group_name = "${azurerm_virtual_machine.test.resource_group_name}"
}
output "public_ip_address" {
value = "${data.azurerm_public_ip.test.*.ip_address}"
}
After terraform apply:
Outputs:
public_ip_address = [
,
]
[1] https://www.terraform.io/docs/providers/azurerm/d/public_ip.html

The reason that you cannot output the multi public IPs is that you do not create multi public IPs. So when you use ${data.azurerm_public_ip.test.*.ip_address} to output them, there no these resources for you.
For the terraform, you could add the count in the resource azurerm_public_ip to create multi public IPs and output them with azurerm_public_ip.test.*.ip_address like this:
variable "count" {
default = "2"
}
...
resource "azurerm_public_ip" "test" {
count = "${var.count}"
name = "test-${count.index}-pip"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
public_ip_address_allocation = "Static"
idle_timeout_in_minutes = 30
tags {
environment = "test-${count.index}"
}
}
...
output "public_ip_address" {
value = "${azurerm_public_ip.test.*.ip_address}"
}
The screenshot of the result like this:
I did the test just create the public. So I change the allocation method into Static and output it with the resource.
If you want to use the data to reference the public IPs. The code would like this:
data "azurerm_public_ip" "test" {
count = "${var.count}"
name = "${element(azurerm_public_ip.test.*.name, count.index)}"
resource_group_name = "${azurerm_resource_group.test.name}"
}
output "public_ip_address" {
value = "${data.azurerm_public_ip.test.*.ip_address}"
}
Hope this will help you. If you need more help please let me know.

So I just ran into the exact same problem with my deployment into Azure.
The above solutions worked( Charles Xu's solution), with one caveat... I had to:
hard-code the name of the resource group,
as well as add a depends on clause to the output block.
I am sure that the above answers are the correct way to go about it however the value of the resource group key in the data object "azurerm_public_ip" needed to be hardcoded...

Related

Can't access attributes on a primitive-typed value (string)

I try to whitelist my apim public ips over my azure function :
apim.tf
data "azurerm_api_management" "main" {
name = "my-apim"
resource_group_name = "my-rg"
}
output "apim_ip" {
value = data.azurerm_api_management.main.public_ip_addresses
}
terraform output
apim_ip = tolist([
"1.2.3.4",
])
func.tf
resource "azurerm_linux_function_app" "az_func" {
name = var.my_func_name
resource_group_name = azurerm_resource_group.main.name
location = var.location
storage_account_name = azurerm_storage_account.main.name
storage_account_access_key = azurerm_storage_account.main.primary_access_key
service_plan_id = azurerm_service_plan.azfunc.id
site_config {
dynamic "ip_restriction" {
for_each = data.azurerm_api_management.main.public_ip_addresses
content {
ip_address = data.azurerm_api_management.main.public_ip_address_id.value
}
}
}
}
On terraform apply I keep having the error message :
Can't access attributes on a primitive-typed value (string).
What am I doing wrong?
data.azurerm_api_management.main.public_ip_address_id is a string, and therefore you cannot cannot access values from it as if it were a map or object type. I believe you meant to access values on the temporary lambda iterator variable assigned from data.azurerm_api_management.main.public_ip_addresses. In that, the usage and syntax would be:
ip_address = site_config.value.id
to access the id value from the current data.azurerm_api_management.main.public_ip_addresses attribute.

how to create generic cosmos db terraform module to add multiple geo_locations

I'm trying to create a module for azure cosmos db using terraform. In my example I wanted geo_location should be more flexible/customized. Which means my failover locations are not standard for all my applications. In one of the application my primary location is WEU but failover is EUS. In other application Primary is EUS but failover location is WEU, WUS2. and so on... So I wanted to use 1 cosmosdb module and the geo_location property should be more self service oriented where infra developers can specify any number of regions they require.
I see that in terraform we have to specify "geo_location" block for every region. This approach will defeat the purpose of having 1 module. Is there anyway can I make it more generic like I explained above?
Any suggestions are helpful.
thanks,
Santosh
If I understand your requirement correctly , You want to build a module for Cosmos DB where the operator will be asked to provide any number of values for geo location and the resource block will create the geo_location blocks accordingly.
In the above case you can create a variable of list type , which will ask the user to provide the values for the same and then use dynamic geo_location block so that it gets configured accordingly. I have tested with the below code :
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "rg" {
name = "cosmos-dbtest"
location = "East US"
}
variable "geo_location" {
type = list
description = "value for Geo Locations"
}
resource "random_integer" "ri" {
min = 10000
max = 99999
}
resource "azurerm_cosmosdb_account" "db" {
name = "tfex-cosmos-db-${random_integer.ri.result}"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
offer_type = "Standard"
kind = "MongoDB"
enable_automatic_failover = true
capabilities {
name = "EnableAggregationPipeline"
}
capabilities {
name = "mongoEnableDocLevelTTL"
}
capabilities {
name = "MongoDBv3.4"
}
capabilities {
name = "EnableMongo"
}
consistency_policy {
consistency_level = "BoundedStaleness"
max_interval_in_seconds = 300
max_staleness_prefix = 100000
}
dynamic "geo_location" {
for_each = var.geo_location
content{
location = geo_location.value
failover_priority = geo_location.key
}
}
}
Output:
OR
If you want to keep the first geo_location same as the location for cosmos DB and then other failover locations , then you can use one static and one dynamic geo_location block like below:
resource "azurerm_cosmosdb_account" "db" {
name = "tfex-cosmos-db-${random_integer.ri.result}"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
offer_type = "Standard"
kind = "MongoDB"
enable_automatic_failover = true
capabilities {
name = "EnableAggregationPipeline"
}
capabilities {
name = "mongoEnableDocLevelTTL"
}
capabilities {
name = "MongoDBv3.4"
}
capabilities {
name = "EnableMongo"
}
consistency_policy {
consistency_level = "BoundedStaleness"
max_interval_in_seconds = 300
max_staleness_prefix = 100000
}
geo_location {
location = azurerm_resource_group.rg.location
failover_priority = 0
}
dynamic "geo_location" {
for_each = var.geo_location
content{
location = geo_location.value
failover_priority = "${geo_location.key + 1}"
}
}
}
Output:

Terraform to create azure subnet delegation for selected subnets using for_each

I want some help on creating delegation on selected azure subnets. My code is as per below details.
Variables defined in my variable file
variable “subnets” {
type = map(any)
}
My tfvar file contains below values
subnets = {
mlops-aue-snt-aks = [“10.255.232.0/24”]
mlops-aue-snt-stg = [“10.255.233.0/26”]
mlops-aue-snt-kv = [“10.255.233.128/27”]
AzureBastionSubnet = [“10.255.233.160/27”]
mlops-aue-snt-shd = [“10.255.234.0/25”]
mlops-aue-snt-db1 = [“10.255.235.0/26”]
mlops-aue-snt-db2 = [“10.255.235.64/26”]
mlops-aue-snt-aci = [“10.255.235.128/26”]
}
This is my code for subnet
resource “azurerm_subnet” “azr_subnet” {
for_each = var.subnets
name = each.key
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = each.value
enforce_private_link_endpoint_network_policies = true
}
All subnets are created with this and it is all fine but now I have a requirement to add service_delegation for mlops-aue-snt-db1 and mlops-aue-snt-db2 and different for other subnets. I am not sure how to achieve this with my existing code. I can’t separate out subnets from the code as it will force to delete existing ones and create new which is not recommended. I did read some posts about using dynamic block to make changes but not sure how to implement it for selective subnets as per my requirement.
Can anyone please suggest how to achieve this in terraform?
You can do this with dynamic blocks and by changing your subnets a bit.
subnets = {
mlops-aue-snt-aks = {
cidr = [“10.255.232.0/24”]
service_delegation = false
}
# the rest in same format
mlops-aue-snt-db1 = {
cidr = [“10.255.235.0/26”]
service_delegation = true
}
mlops-aue-snt-db2 = {
cidr = [“10.255.235.64/26”]
service_delegation = true
}
# ...
}
then
resource "azurerm_subnet" "azr_subnet" {
for_each = var.subnets
name = each.key
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = each.value.cidr
enforce_private_link_endpoint_network_policies = true
dynamic "delegation" {
for_each = each.value.service_delegation == "true" ? [1] : []
content {
name = "delegation"
service_delegation {
name = "Microsoft.ContainerInstance/containerGroups"
actions = ["Microsoft.Network/virtualNetworks/subnets/join/action", "Microsoft.Network/virtualNetworks/subnets/prepareNetworkPolicies/action"]
}
}
}
}
Check out this repo. This is not my work and I am not taking credit for it. I have just decided to share it so that it can be useful for devs looking for a solution for this common problem.

How to output ID from one resource which contains map variable

I'm new in Terraform and I have silly question.I want to create NAT gateway and I need ID of one subnet:
resource "azurerm_subnet" "this" {
for_each = var.subnet_prefixes
name = each.key
resource_group_name = var.subnetRGname
virtual_network_name = azurerm_virtual_network.this.name
address_prefixes = each.value
}
variable subnet_prefixes {
type = map
default = {
"PublicSubnet"=["10.0.1.0/24"]
"PrivateSubnet"=["10.0.2.0/24"]
}
}
output "publicsubnet" {
value = map ("private", azurerm_subnet.this.id)
/* {
for s in azurerm_subnet.this : s.name => private.id
} */
}
In first block I'm creating subnets from "subnet_prefixes" map variables.
But then I want to output only "PublicSubnet" ID so I can join it to my NAT gateway.
Is there any simple way to do this?
Thank you very much for your help.
EDIT:
Well I tried to do something about it and it kinda works while using count, but it is not what I expected:
resource "azurerm_subnet" "this" {
count = length(var.subnet_prefixes)
name = var.subnetnames[count.index]
resource_group_name = var.subnetRGname
virtual_network_name = azurerm_virtual_network.this.name
address_prefixes = [var.subnet_prefixes[count.index]]
}
variable subnet_prefixes {
type = list(string)
default = ["10.0.1.0/24","10.0.2.0/24"]
}
variable subnetnames {
type = list(string)
default = ["SubnetA","SubnetB"]
}
output "publicsubnet" {
value = azurerm_subnet.this.*.id
}
module "NAT" {
source = "./Modules/NAT"
NATname = "${var.RGName}-NAT"
NATRGlocation = module.Resource_Group.resource_group_location
NATRGname = module.Resource_Group.resource_group_name
publicIPid = module.VNET.publicIP
publicsubnetID = module.VNET.publicsubnet[0]
}
I'm just wondering, as you can see on main module i need to specify index of subnet, and I wanted to export only ID that somehow refers to name like "Public*". Is there any way to do it, or maybe could you please tell me how to improve this code?
Since you've used for_each in your azurerm_subnet based on map of var.subnet_prefixes, you can refer to individuals subnets using keys from the map.
So public subnet id should be be:
output "publicsubnet" {
value = azurerm_subnet.this["PublicSubnet"].id
}

how to call the resource group in virtual network the resource group parameter comes to CSV file

I am trying to access the resource group name in virtual network in same code, have define some parameters using CSV file but when I am calling that parameter in code its showing error,
please check below code
provider "azurerm" {
features {}
}
locals {
group_names = csvdecode(file("./test.csv"))
}
//Create a resource group and nsg
resource "azurerm_resource_group" "Customer" {
count = length(local.group_names)
name = local.group_names[count.index].resource_group_name
location = local.group_names[count.index].region
}
//Create a Vnet
resource "azurerm_virtual_network" "Customer" {
count = length(local.group_names)
name = local.group_names[count.index].virtual_network_name
location = azurerm_resource_group.Customer.location
resource_group_name = azurerm_resource_group.Customer.name
address_space = [local.group_names[count.index].address_space]
}
//create Subnet
resource "azurerm_subnet" "Customer" {
count = length(local.group_names)
name = local.group_names[count.index].subnet_name
resource_group_name = azurerm_resource_group.Customer.name
virtual_network_name = azurerm_virtual_network.Customer.name
address_prefix = local.group_names[count.index].address_prefix_subnet
}
following error is occured
As the error show, you could change the code like this
resource "azurerm_virtual_network" "Customer" {
count = length(local.group_names)
name = local.group_names[count.index].virtual_network_name
location = azurerm_resource_group.Customer[count.index].location # add [count.index]
resource_group_name = azurerm_resource_group.Customer[count.index].name # add [count.index]
address_space = [local.group_names[count.index].address_space]
}

Resources