Is it possible to dynamically create "azurerm" providers? - terraform

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.

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

Is there a way to exclude resources from default tags in terraform

I am in the process of adding default tags to all our resources in terraform with the following:
provider "aws" {
region = var.region
default_tags {
tags = {
terraform_managed = "true"
}
}
}
Now it seems to be wanting to recreate some of my EC2 instances due to the user data being updated which is populated by a template file.
Is there a way to ignore data blocks from the default tags:
data "template_file" "ec2_user_data" {
template = file("${path.module}/templates/user_data.tpl")
vars = {
region = var.region
vr._version = var.vr_version
}
}
~ user_data = "110437fa31b8fe5795165cf9973be6c5b" -> (known after apply) # forces replacement
This is the only change to the code so nothing else could be causing this.
Thanks for any help in advance.
Changing from data.template_file to the templatefile() fixed the issue.
Thanks #matt-schuchard
Since Terraform data sources are defined outside and managed outside of Terraform, default_tags in the AWS Terraform provider will have no effect on data sources.
The change shown in the plan is a change in the user_data.tpl file

Combine terraform locals variables

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 {}
}

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 depends_on with modules

I'm new at terraform and I created a custom azure policies on module structure.
each policy represents a custom module.
One of the modules that I have created is enabling diagnostics logs for any new azure resource created.
but, I need a storage account for that. (before enabling the diagnostics settings how can I implement "depends_on"? or any other methods?
I want to create first the storage account and then the module of diagnostics settings.
on the main.tf (where calling all the other modules) or inside the resource (module)?
Thanks for the help!! :)
this below code represents the main.tf file:
//calling the create storage account name
module "createstorageaccount" {
source = "./modules/module_create_storage_account"
depends_on = [
"module_enable_diagnostics_logs"
]
}
this one represents the create storage account module
resource "azurerm_resource_group" "management" {
name = "management-rg"
location = "West Europe"
}
resource "azurerm_storage_account" "test" {
name = "diagnostics${azurerm_resource_group.management.name}"
resource_group_name = "${azurerm_resource_group.management.name}"
location = "${azurerm_resource_group.management.location}"
account_tier = "Standard"
account_replication_type = "LRS"
tags = {
environment = "diagnostics"
}
}
depends_on = [
"module_enable_diagnostics_logs"
]
In most cases, the necessary dependencies just occur automatically as a result of your references. If the configuration for one resource refers directly or indirectly to another, Terraform automatically infers the dependency between them without the need for explicit depends_on.
This works because module variables and outputs are also nodes in the dependency graph: if a child module resource refers to var.foo then it indirectly depends on anything that the value of that variable depends on.
For the rare situation where automatic dependency detection is insufficient, you can still exploit the fact that module variables and outputs are nodes in the dependency graph to create indirect explicit dependencies, like this:
variable "storage_account_depends_on" {
# the value doesn't matter; we're just using this variable
# to propagate dependencies.
type = any
default = []
}
resource "azurerm_storage_account" "test" {
name = "diagnostics${azurerm_resource_group.management.name}"
resource_group_name = "${azurerm_resource_group.management.name}"
location = "${azurerm_resource_group.management.location}"
account_tier = "Standard"
account_replication_type = "LRS"
tags = {
environment = "diagnostics"
}
# This resource depends on whatever the variable
# depends on, indirectly. This is the same
# as using var.storage_account_depends_on in
# an expression above, but for situations where
# we don't actually need the value.
depends_on = [var.storage_account_depends_on]
}
When you call this module, you can set storage_account_depends_on to any expression that includes the objects you want to ensure are created before the storage account:
module "diagnostic_logs" {
source = "./modules/diagnostic_logs"
}
module "storage_account" {
source = "./modules/storage_account"
storage_account_depends_on = [module.diagnostic_logs.logging]
}
Then in your diagnostic_logs module you can configure indirect dependencies for the logging output to complete the dependency links between the modules:
output "logging" {
# Again, the value is not important because we're just
# using this for its dependencies.
value = {}
# Anything that refers to this output must wait until
# the actions for azurerm_monitor_diagnostic_setting.example
# to have completed first.
depends_on = [azurerm_monitor_diagnostic_setting.example]
}
If your relationships can be expressed by passing actual values around, such as by having an output that includes the id, I'd recommend preferring that approach because it leads to a configuration that is easier to follow. But in rare situations where there are relationships between resources that cannot be modeled as data flow, you can use outputs and variables to propagate explicit dependencies between modules too.
module dependencies are now supported in Terraform 13, this is currently at the release candidate stage.
resource "aws_iam_policy_attachment" "example" {
name = "example"
roles = [aws_iam_role.example.name]
policy_arn = aws_iam_policy.example.arn
}
module "uses-role" {
# ...
depends_on = [aws_iam_policy_attachment.example]
}
Using depends_on at resource level is different from using depends_on at inter-module level i found very simple way to do to it at module level
module "eks" {
source = "../modules/eks"
vpc_id = module.vpc.vpc_id
vpc_cidr = [module.vpc.vpc_cidr_block]
public_subnets = flatten([module.vpc.public_subnets])
private_subnets_id = flatten([module.vpc.private_subnets])
depends_on = [module.vpc]
}
i created dependencies directly with module simple as simplest no complex relation reequired

Resources