Combine terraform locals variables - terraform

Can you guide me on how to combine locals? This is not working though some online docs advise to frame this way. Appreciate your help, thanks!
# see README.md for developer guide
# prepare subscription where resources are created
locals {
location_code = "weu"
environment_code = "test"
}
locals {
kv_name = "oamp-kv-${local.environment_code}-${location_code}"
ai_name = "oamp-ai-${local.environment_code}-${location_code}"
}
# prepare azure rm configuration
provider "azurerm" {
version = "~>2.15.0"
use_msi = true
features {}
}
Validation
Error: Invalid reference
on main.tf line 20, in locals:
20: kv_name = "oamp-kv-${local.environment_code}-${location_code}"
A reference to a resource type must be followed by at least one attribute
access, specifying the resource name.

You can reference local variables into locals as it is written in the documentation.
As shown above, local values can be referenced from elsewhere in the
module with an expression like local.common_tags, and locals can
reference each other in order to build more complex values from
simpler ones.
The error come from the fact you need to prefix with an attribute
access your resource. However you did not prefix location_code.
In you code you miss to prefix access attribute local before location_code.
What you need to do is prefix correctly your variables :
# see README.md for developer guide
# prepare subscription where resources are created
locals {
location_code = "weu"
environment_code = "test"
}
locals {
kv_name = "oamp-kv-${local.environment_code}-${local.location_code}"
ai_name = "oamp-ai-${local.environment_code}-${local.location_code}"
}
# prepare azure rm configuration
provider "azurerm" {
version = "~>2.15.0"
use_msi = true
features {}
}

Related

Using azure azurerm_resource_provider_registration with newly created subscription

I am working on creating Azure landing zone and part of that is to enable/disable resource providers on the newly created subscriptions.
I have tried to used alias with a variable but i am getting error that i cant use variable in an alias so is there any way through which i can use this feature on multiple subscription
This is my code main.tf
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=3.0.0"
}
}
}
#list of providers i want to register
locals {
# List is compiled from here
# https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/azure-services-resource-providers
provider_list = [
"Microsoft.Storage"
]
provider_map = { for p in local.provider_list : p => p }
}
# Registering a default provider here and skipping registration
# as i will do it later
provider "azurerm" {
features {}
skip_provider_registration = true
}
# I am creating a subscription here with same alias as the name
# the subscription is being created under and EA enrollment but
# any type of subscription will do
resource "azurerm_subscription" "feature_subscription" {
billing_scope_id = "/providers/Microsoft.Billing/billingAccounts/xxx/enrollmentAccounts/xx"
alias = var.temp_alias # "test-provider-registration"
subscription_name = "test-provider-registration"
}
#this is what i have created to point out my azurerm_resource_provider_registration
#module i am using variable in alias which is failing
provider "azurerm" {
alias = var.temp_alias
subscription_id = azurerm_subscription.feature_subscription.id
features {
}
skip_provider_registration = true
}
#module through which i am registering the resource providers
module "azurerm_resource_provider_registration-provider" {
source = "../modules/azurerm_resource_provider_registration"
providers = {
azurerm = azurerm.test-provider-registration
}
feature_list = local.provider_map
}
#the module code is mentioned here
#resource "azurerm_resource_provider_registration" "provider" {
# for_each = var.feature_list
# name = each.value
#}
I am getting this error when i run it
There are some problems with the configuration, described below.
The Terraform configuration must be valid before initialization so that
Terraform can determine which modules and providers need to be installed.
╷
Error: Variables not allowed
│
On main.tf line 25: Variables may not be used here.
╵
╷
Error: Unsuitable value type
│
On main.tf line 25: Unsuitable value: value must be known
There is a workaround available like using this
resource "null_resource" "provider_registration" {
for_each = local.provider_map
provisioner "local-exec" {
command = "az account set --subscription ${data.azurerm_subscription.subscription.subscription_id} && az provider register --namespace ${each.value}"
}
}
but i want to use the state file for the resource registration if possible as i have more subscriptions in a loop
Error: Unsuitable value type | Unsuitable value: value must be known
Need to check:
This problem usually occurs with module sources or versions. When invoking a module, using variables instead of passing direct values for the source and some other arguments causes this error. terraform initdoesn't take variable inputs with modules in backend state configuration.
Note: And also include required version for modules.
To make it work, use direct values for module block instead of accessing from other resources.
Pass provider explicitly to make it work as detailed in terraform providers.
providers = {
azurerm = azurerm.test-provider-registration
}
feature_list = local.provider_map
}
}
module "azurerm_resource_provider_registration-provider" {
source ="../modules/azurerm_resource_provider_registration"
version = <required version> //Use latest one
After checking the above conditions, I tried same in my environment and it was initialized successfully:
Refer aliasing for the deployment in multiple subscriptions detailed by #Jeff Brown

Using terraform variable in hcl extention ( vault )

I'am tring to do automation for path and policies creation in vault.
Do you know how I can proceed please ? variables declared in terraform are not reconized in .hcl file.
I tried to rename my file client-ro-policy.hcl to client-ro-policy.tf but I have same issue
Varibales is recognized in file with .tf extention
Thanks
main.tf
# Use Vault provider
provider "vault" {
# It is strongly recommended to configure this provider through the
# environment variables:
# - VAULT_ADDR
# - VAULT_TOKEN
# - VAULT_CACERT
# - VAULT_CAPATH
# - etc.
}
acl-ro-policy.hcl
path "${var.client[0]}/k8s/preprod/*" {
capabilities = ["read"]
}
policies.tf
#---------------------
# Create policies
#---------------------
# Create 'client' policy
resource "vault_policy" "ro-client" {
name = "${var.client[0]}_k8s_preprod_ro"
policy = file("./hcl-ro-policy.tf")
}
variables.tf
variable "client" {
type = list(string)
}
variables.tfvars
client = ["titi", "toto","itutu"]
Result in vault:
Even though Terraform and Vault both use HCL as the underlying syntax of their respective configuration languages, their language interpreters are totally separate and so the Vault policy language implementation cannot make direct use of any values defined in the Terraform language.
Instead, you'll need to use the Terraform language to construct a suitable configuration for Vault. Vault supports a JSON variant of its policy language in order to make it easier to programmatically generate it, and so you can use Terraform's jsonencode function to build a JSON-based policy from the result of a Terraform expression, which may itself include references to values elsewhere in Terraform.
For example:
locals {
vault_ro_policy = {
path = {
"${var.client[0]}/k8s/preprod/*" = {
capabilities = ["read"]
}
}
}
}
resource "vault_policy" "ro-client" {
name = "${var.client[0]}_k8s_preprod_ro"
policy = jsonencode(local.var_ro_policy)
}
The value of local.vault_ro_policy should encode to JSON as follows, assuming that var.client[0] has the value "example":
{
"path": {
"example/k8s/preprod/*": {
"capabilities": ["read"]
}
}
}
Assuming that this is valid Vault JSON policy syntax (which I've not verified), this should be accepted by Vault as a valid policy. If I didn't get the JSON policy syntax exactly right then hopefully you can see how to adjust it to be valid; my expertise is with Terraform, so I focused on the Terraform language part here.

Is it possible to dynamically create "azurerm" providers?

I would like to create multiple azure_rm providers, from a variable list.
Trying with "count" is not possible, I got the message "reserved for future usage".
Technicaly describing, the section that I would like to dynamic create is:
provider "azurerm" {
features {}
subscription_id = "1d1a56ed-681f-xxxx-xxxx-xxxxxxxxxxxx"
alias = "spoke1"
}
I tried also with for_each, but also not possible, since for_each creates only a section, not a complete "provider" section.
# To improve... try to use dynamic block
# It looks to be not possible. Could be necessary to auto-generate this scetion with external script
# provider "azurerm" {
# features {}
# dynamic "spoke" {
# for_each = var.spoke_networks
# content {
# subscription_id = spoke.value["subscription_id"]
# alias = spoke.value["subscription_alias"]
# }
# }
# }
What I would like to achieve is to just feed my variable(spoke_networks) with a new entry, and terraform creates a new provider.
the variable file stucture should be something like this:
variable "spoke_networks" {
description = "List with the properties of the Spoke VNETs"
type = list(object({subscription_alias=string ,subscription_id=string , resource_group_name=string , vnet_name=string }))
}
Thank you,
VS
You can't do this with terraform. But possibly you could do it with terragrunt.

Reference multiple aws_instance in terraform for template output

We want to deploy services into several regions.
Looks like because of the aws provider, we can't just use count or for_each, as the provider can't be interpolated. Thus I need to set this up manually:
resource "aws_instance" "app-us-west-1" {
provider = aws.us-west-1
#other stuff
}
resource "aws_instance" "app-us-east-1" {
provider = aws.us-east-1
#other stuff
}
I would like when running this to create a file which contains all the IPs created (for an ansible inventory).
I was looking at this answer:
https://stackoverflow.com/a/61788089/169252
and trying to adapt it for my case:
resource "local_file" "app-hosts" {
content = templatefile("${path.module}/templates/app_hosts.tpl",
{
hosts = aws_instance[*].public_ip
}
)
filename = "app-hosts.cfg"
}
And then setting up the template accordingly.
But this fails:
Error: Invalid reference
on app.tf line 144, in resource "local_file" "app-hosts":
122: hosts = aws_instance[*].public_ip
A reference to a resource type must be followed by at least one attribute
access, specifying the resource name
I am suspecting that I can't just reference all the aws_instance defined as above like this. Maybe to refer to all aws_instance in this file I need to use a different syntax.
Or maybe I need to use a module somehow. Can someone confirm this?
Using terraform v0.12.24
EDIT: The provider definitions use alias and it's all in the same app.tf, which I was naively assuming to be able to apply in one go with terraform apply (did I mention I am a beginner with terraform?):
provider "aws" {
alias = "us-east-1"
region = "us-east-1"
}
provider "aws" {
alias = "us-west-1"
region = "us-west-1"
}
My current workaround is to not do a join but simply listing them all individually:
{
host1 = aws_instance.app-us-west-1.public_ip
host2 = aws_instance.app-us-east-1.public_ip
# more hosts
}

Terraform module output not resolving for input variables in other module

Terraform Version
Terraform v0.11.11
+ provider.azurerm v1.21.0
Terraform Configuration Files
I have left many required fields for brevity (all other config worked before I added the connection strings).
# modules/function/main.tf
variable "conn-value" {}
locals {
conn = "${map("name", "mydb", "value", "${var.conn-value}", "type", "SQLAzure")}"
}
resource "azurerm_function_app" "functions" {
connection_string = "${list(local.conn)}"
# ...
}
# modules/db/main.tf
# ... other variables declared
resource "azurerm_sql_server" "server" {
# ...
}
output "connection-string" {
value = "Server=tcp:${azurerm_sql_server.server.fully_qualified_domain_name},1433;Initial Catalog=${var.catalog};Persist Security Info=False;User ID=${var.login};Password=${var.login-password};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=200;"
}
# main.tf
module "my_db" {
source = "modules/db"
}
module "my_app" {
source = "modules/function"
conn-value = "${module.my_db.connection-string}"
# ...
}
Expected Behavior on terraform plan
The module.my_db.connection-string output resolves to a string when passed to the my_app conn-value variable and is able to be used in the map/list passed to the azurerm_function_app.functions.connection_string variable.
Actual Behavior on terraform plan
I get this error:
module.my_app.azurerm_function_app.functions: connection_string: should be a list
If I replace "${var.conn-value}" in the modules/function/main.tf locals with just a string, it works.
Update
In response to to this comment, I updated the script above with the connection string construction.
I finally found the GitHub issue that references the problem I am having (I found the issue through this gist comment). This describes the problem exactly:
Assigning values to nested blocks is not supported, but appears to work in certain cases due to a number of coincidences...
This limitation is in <= v0.11, but is apparently fixed in v0.12 with the dynamic block.

Resources