I have this code in TF for creating App Insight Resource.
The name for creating the App Insight is being done by our internal names module.
The output of the name for App Insight is of the format -->
(Resource Group Name)-(Your own custom value)-dev-cus-001
Now we have 3 resources for App Insight with 3 custom values defined in the Locals block as shown below:
locals {
application-insights = [
"masterdata",
"inboundintegration",
"intgr-func",
]
}
module "names-application-insights" {
version = "~> 1.0"
source = "My Module URL"
for_each = toset(local.application-insights)
resource_type = "application-insights"
resource_location = var.resource_group_location
resource_environment = var.resource_group_environment
resource_name = format("%s-%s", var.resource_group_name, each.value)
resource_count = var.resource_group_count
}
resource "azurerm_application_insights" "default" {
for_each = toset(local.application-insights)
location = data.azurerm_resource_group.default.location
resource_group_name = data.azurerm_resource_group.default.name
name = module.names-application-insights[each.value].results
application_type = "web"
}
Now i have been asked to remove the custom value "masterdata" from the name of the first App Insight resource : (rg)-masterdata-dev-cus-001
So how do i remove the "masterdata" value from just one App Insights and ensure the other two App Insights that are created with two Locals : "inboundintegration" & "intgr-func" are not affected
If i remove the first custom value "masterdata" from the Locals block, it will throw error right if i run the code after removing that value from Locals?
How to edit this resource name then.
As I see you use for_each to create the application insights. Then the only thing you can do is to remove the custom value masterdata from the Locals and it won't affect the other two resources. It means you will only create two application insights with the names "inboundintegration" & "intgr-func".
Related
I have defined resource to provision databricks workspace on Azure using Terraform as follows which consumes the list ( of inputs from tfvar file for # of workspaces) and provision them.
resource "azurerm_databricks_workspace" "workspace" {
for_each = { for r in var.databricks_workspace_list : r.workspace_nm => r}
name = each.key
resource_group_name = each.value.resource_group_name
location = each.value.location
sku = "standard"
tags = {
Environment = "Dev"
}
}
I am trying to create additional resource as below
resource "databricks_instance_pool" "smallest_nodes" {
instance_pool_name = "Smallest Nodes"
min_idle_instances = 0
max_capacity = 300
node_type_id = data.databricks_node_type.smallest.id // data block is defined
idle_instance_autotermination_minutes = 10
}
To create instance pool, I need to pass workspace id in databricks provider block as below
provider "databricks" {
azure_client_id= *******
azure_client_secret= *******
azure_tenant_id= *******
azure_workspace_resource_id = azurerm_databricks_workspace.workspace.id
}
But when I do terraform plan, it fails with below error
Missing resource instance key
azure_workspace_resource_id = azurerm_databricks_workspace.workspace.id
Because azure_workspace_resource_id = azurerm_databricks_workspace has for_each set, its attribute must be accessed on specific instances.
For example, to correlate indices , use :
azurerm_databricks_workspace[each.key]
I couldnt use for_each in provider block, also not able to find out way to index workspace id in provider block.
Appreciate your inputs.
TF version : 0.13
Azure RM : 3.10.0
Databricks : 0.5.7
The problem is that you can create multiple workspaces when you're using for_each in the azurerm_databricks_workspace resource. But your provider block is trying to refer to a "generic" resource instance, so it's complaining.
The solution here would be either:
Remove for_each if you're creating just one workspace
instead of azurerm_databricks_workspace.workspace.id, you need to refer azurerm_databricks_workspace.workspace[<name>].id where the <name> is the specific instance of Databricks from the list of workspaces.
P.S. Your databricks_instance_pool resource doesn't have explicit depends_on, so the operation will fail with authentication error as described here.
I would create a sample azure web app using Terraform.
I use this code to create the resoucre.
This is my main.tf file :
resource "azurerm_resource_group" "rg" {
name = var.rgname
location = var.rglocation
}
resource "azurerm_app_service_plan" "plan" {
name = var.webapp_plan_name
location = var.rglocation
resource_group_name = var.rgname
sku {
tier = var.plan_settings["tier"]
size = var.plan_settings["size"]
capacity = var.plan_settings["capacity"]
}
}
resource "azurerm_app_service" "webapp" {
name = var.webapp_name
location = var.rglocation
resource_group_name = var.rgname
app_service_plan_id = azurerm_app_service_plan.plan.id
}
and this is the variable.tf
# variables for Resource Group
variable "rgname" {
description = "(Required)Name of the Resource Group"
type = string
default = "example-rg"
}
variable "rglocation" {
description = "Resource Group location like West Europe etc."
type = string
default = "eastus2"
}
# variables for web app plan
variable "webapp_plan_name" {
description = "Name of webapp"
type = string
default = "XXXXXXXXXx"
}
variable "plan_settings" {
type = map(string)
description = "Definition of the dedicated plan to use"
default = {
kind = "Linux"
size = "S1"
capacity = 1
tier = "Standard"
}
}
variable "webapp_name" {
description = "Name of webapp"
type = string
default = "XXXXXXXX"
}
The terraform apply --auto-approve show a error :
Error: creating/updating App Service Plan "XXXXXXXXXXX" (Resource Group "example-rg"): web.AppServicePlansClient#CreateOrUpdate: Failure sending request: StatusCode=0 -- Original Error: Code="ResourceGroupNotFound" Message="Resource group 'example-rg' could not be found."
but in Azure Portal , the ressource group is created
what's wrong in my code ?
Can i hold on Terraform to check if resource is created or not before pass to next resource ?
You need to reference the resources to indicate the dependency between them to terraform, so that it can guarantee, that the resource group is created first, and then the other resources. The error indicates, that the resource group does not exist, yet. Your code tries to create the ASP first and then the RG.
resource "azurerm_resource_group" "rg" {
name = var.rgname
location = var.rglocation
}
resource "azurerm_app_service_plan" "plan" {
...
resource_group_name = azurerm_resource_group.rg.name
...
}
... but I can't use variable?
To answer that question and give more detail about the current issue, what's happening in your original code is that you're not referencing terraform resources in a way that terraform can use to create its dependency graph.
if var.rgname equals <my-rg-name> and azurerm_resource_group.rg.name also equals <my-rg-name> then technically, that's the same, right?
Well, no, not necessarily. They indeed have the same value. The difference is that the first one just echos the value. The second one contains the same value but it's also instruction to terraform saying, "Hey, wait a minute. We need the name value from azurerm_resource_group.rg so let me make sure I set that up first, and then I'll provision this resource"
The difference is subtle but important. Using values in this way lets Terraform understand what it has to provision first and lets it create its dependency graph. Using variables alone does not. Especially in large projects, alway try to use the resource variable value instead of just input variables which will help avoid unnecessary issues.
It's quite common to use an input variable to define certain initial data, like the name of the resource group as you did above. After that, however, every time resource_group_name is referenced in the manifests you should always use terraform generated value, so resource_group_name = azurerm_resource_group.rg.name
I have several Azure resources that are created using the for_each property and then those resources have an Application Insights resource created using for_each as well.
Here is the code that creates the azurerm_application_insights:
resource "azurerm_application_insights" "applicationInsights" {
for_each = toset(keys(merge(local.appServices, local.functionApps)))
name = lower(join("-", ["wb", var.deploymentEnvironment, var.location, each.key, "ai"]))
location = var.location
resource_group_name = azurerm_resource_group.rg.name
application_type = "web"
lifecycle {
ignore_changes = [tags]
}
}
I've noticed that every time we run a terraform plan against some environments, we are always seeing Terraform report a "change" to the APPINSIGHTS_INSTRUMENTATIONKEY value. When I compare this value in the app settings key/value list to the actual AI instrumentation key that was created for it, it does match.
Terraform will perform the following actions:
# module.primaryRegion.module.functionapp["fnapp1"].azurerm_function_app.fnapp will be updated in-place
~ resource "azurerm_function_app" "fnapp" {
~ app_settings = {
# Warning: this attribute value will be marked as sensitive and will
# not display in UI output after applying this change
~ "APPINSIGHTS_INSTRUMENTATIONKEY" = (sensitive)
# (1 unchanged element hidden)
Is this a common issue with other people? I would think that the instrumentation key would never change especially since Terraform is what created all of these Application Insights resources and assigns it to each application.
This is how I associate each Application Insights resource to their appropriate application with a for_each property
module "webapp" {
for_each = local.appServices
source = "../webapp"
name = lower(join("-", ["wb", var.deploymentEnvironment, var.location, each.key, "app"]))
location = var.location
resource_group_name = azurerm_resource_group.rg.name
app_service_plan_id = each.value.app_service_plan_id
app_settings = merge({"APPINSIGHTS_INSTRUMENTATIONKEY" = azurerm_application_insights.applicationInsights[each.key].instrumentation_key}, each.value.app_settings)
allowed_origins = each.value.allowed_origins
deploymentEnvironment = var.deploymentEnvironment
}
I'm wondering if the merge is just reordering the list of key/values in the app_settings for the app, and Terraform detects that as some kind of change and the value itself isn't changing. This is the only way I know how to assign a bunch of Application Insights resources to many web apps using for_each to reduce configuration code.
Use only the Site_config block to solve the issue
Example
resource "azurerm_windows_function_app" "function2" {
provider = azurerm.private
name = local.private.functionapps.function2.name
resource_group_name = local.private.rg.app.name
location = local.private.location
storage_account_name = local.private.functionapps.storageaccount.name
storage_account_access_key = azurerm_storage_account.function_apps_storage.primary_access_key
service_plan_id = azurerm_service_plan.app_service_plan.id
virtual_network_subnet_id = lookup(azurerm_subnet.subnets, "appservice").id
https_only = true
site_config {
application_insights_key = azurerm_application_insights.appinisghts.instrumentation_key
}
}
I'm getting the following error when trying to do a plan or an apply on a terraform script.
Error: Invalid count argument
on main.tf line 157, in resource "azurerm_sql_firewall_rule" "sqldatabase_onetimeaccess_firewall_rule":
157: count = length(split(",", azurerm_app_service.app_service.possible_outbound_ip_addresses))
The "count" value depends on resource attributes that cannot be determined
until apply, so Terraform cannot predict how many instances will be created.
To work around this, use the -target argument to first apply only the
resources that the count depends on.
I understand this is falling over because it doesn't know the count for the number of firewall rules to create until the app_service is created. I can just run the apply with an argument of -target=azurerm_app_service.app_service then run another apply after the app_service is created.
However, this isn't great for our CI process, if we want to create a whole new environment from our terraform scripts we'd like to just tell terraform to just go build it without having to tell it each target to build in order.
Is there a way in terraform to just say go build everything that is needed in order without having to add targets?
Also below is an example terraform script that gives the above error:
provider "azurerm" {
version = "=1.38.0"
}
resource "azurerm_resource_group" "resourcegroup" {
name = "rg-stackoverflow60187000"
location = "West Europe"
}
resource "azurerm_app_service_plan" "service_plan" {
name = "plan-stackoverflow60187000"
resource_group_name = azurerm_resource_group.resourcegroup.name
location = azurerm_resource_group.resourcegroup.location
kind = "Linux"
reserved = true
sku {
tier = "Standard"
size = "S1"
}
}
resource "azurerm_app_service" "app_service" {
name = "app-stackoverflow60187000"
resource_group_name = azurerm_resource_group.resourcegroup.name
location = azurerm_resource_group.resourcegroup.location
app_service_plan_id = azurerm_app_service_plan.service_plan.id
site_config {
always_on = true
app_command_line = ""
linux_fx_version = "DOCKER|nginxdemos/hello"
}
app_settings = {
"WEBSITES_ENABLE_APP_SERVICE_STORAGE" = "false"
}
}
resource "azurerm_sql_server" "sql_server" {
name = "mysqlserver-stackoverflow60187000"
resource_group_name = azurerm_resource_group.resourcegroup.name
location = azurerm_resource_group.resourcegroup.location
version = "12.0"
administrator_login = "4dm1n157r470r"
administrator_login_password = "4-v3ry-53cr37-p455w0rd"
}
resource "azurerm_sql_database" "sqldatabase" {
name = "sqldatabase-stackoverflow60187000"
resource_group_name = azurerm_sql_server.sql_server.resource_group_name
location = azurerm_sql_server.sql_server.location
server_name = azurerm_sql_server.sql_server.name
edition = "Standard"
requested_service_objective_name = "S1"
}
resource "azurerm_sql_firewall_rule" "sqldatabase_firewall_rule" {
name = "App Service Access (${count.index})"
resource_group_name = azurerm_sql_database.sqldatabase.resource_group_name
server_name = azurerm_sql_database.sqldatabase.name
start_ip_address = element(split(",", azurerm_app_service.app_service.possible_outbound_ip_addresses), count.index)
end_ip_address = element(split(",", azurerm_app_service.app_service.possible_outbound_ip_addresses), count.index)
count = length(split(",", azurerm_app_service.app_service.possible_outbound_ip_addresses))
}
To make this work without the -target workaround described in the error message requires reframing the problem in terms of values that Terraform can know only from the configuration, rather than values that are generated by the providers at apply time.
The trick then would be to figure out what values in your configuration the Azure API is using to decide how many IP addresses to return, and to rely on those instead. I don't know Azure well enough to give you a specific answer, but I see on Inbound/Outbound IP addresses that this seems to be an operational detail of Azure App Services rather than something you can control yourself, and so unfortunately this problem may not be solvable.
If there really is no way to predict from configuration how many addresses will be in possible_outbound_ip_addresses, the alternative is to split your configuration into two parts where one depends on the other. The first would configure your App Service and anything else that makes sense to manage along with it, and then the second might use the azurerm_app_service data source to retrieve the data about the assumed-already-existing app service and make firewall rules based on it.
Either way you'll need to run Terraform twice to make the necessary data available. An advantage of using -target is that you only need to do a funny workflow once during initial bootstrapping, and so you could potentially do the initial create outside of CI to get the objects initially created and then use CI for ongoing changes. As long as the app service object is never replaced, subsequent Terraform plans will already know how many IP addresses are set and so should be able to complete as normal.
I'm new to Terraform and trying to wrap my head around the use of output variables. we are on AKS, and I'm deploying the following resources: resource group, log analytics workspace, Azure Kubernetes. When Log analytics is deployed, I capture the workspace ID into an output variable. Now, when Terraform deploys Kubernetes, it needs to know the workspace ID, how can I pass the output value to the addon_profile (last line in the code below)?
Error:
environment = "${log_analytics_workspace_id.value}"
A managed resource "log_analytics_workspace_id" "value" has not been declared in the root module.
Code:
resource "azurerm_resource_group" "test" {
name = "${var.log}"
location = "${var.location}"
}
resource "azurerm_log_analytics_workspace" "test" {
name = "${var.logname}"
location = "${azurerm_resource_group.loganalytics.location}"
resource_group_name = "${azurerm_resource_group.loganalytics.name}"
sku = "PerGB2018"
retention_in_days = 30
}
**output "log_analytics_workspace_id" {
value = "${azurerm_log_analytics_workspace.test.workspace_id}"
}**
....................................................
addon_profile {
oms_agent {
enabled = true
**log_analytics_workspace_id = "${log_analytics_workspace_id.value}"**
}
}
Terraform's output values are like the "return values" of a module. In order to declare and use the log_analytics_workspace_id output value, you would need to put all of the code for the creation of the resource group, log analytics workspace, and Azure Kubernetes infrastructure into a single Terraform module, and then reference the output value from outside of the module:
# declare your module here, which contains creation code for all your Azure infrastructure + the output variable
module "azure_analytics" {
source = "git::ssh://git#github.com..."
}
# now, you can reference the output variable in your addon_profile from outside the module:
addon_profile {
oms_agent {
enabled = true
log_analytics_workspace_id = "${module.azure_analytics.log_analytics_workspace_id}"
}
}
On the other hand, if you just want to use the workspace_id value from your azurerm_log_analytics_workspace within the same code, just reference it like azurerm_log_analytics_workspace.test.workspace_id.