I have below two environments terraform files
devenv_variables.tfvars
testenv_variables.tfvars
Here is devenv_variables.tfvars
location = "westeurope"
resource_group_name = "devenv-cloudresources-rg"
Here is testenv_variables.tfvars
location = "westeurope"
resource_group_name = "testenv-cloudresources-rg"
Here is my main.tf
# Configure the Microsoft Azure Provider
provider "azurerm" {
version = "=2.0.0"
features {}
subscription_id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
provider "azurerm" {
alias = "testenv"
subscription_id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
# Create a resource group
resource "azurerm_resource_group" {
provider = "azurerm.testenv" //How do I pass provider based on variables here?
name = "${var.resource_group_name}"
location = "${var.location}"
}
My requirement is, based on passed tfvar file as parameter, it should choose the subscription.
terraform apply -var-file="devenv_variables.tfvars"
when I type below command resource shall create in test environment
terraform apply -var-file="testenv_variables.tfvars"
I think, I need to define client id, and password to login to respective subscriptions.
tfvars files should only contain the values of variables.
The declaration of variables should happen in regular tf files.
variables.tf
variable "location" {
type = "string"
description = "The azure location where the resources is created"
}
devenv_variables.tfvars
location = "West Europe"
This tutorial can also help you with some more information and examples.
All you have to do this just make same variable name in both files. when you run the command terraform plan–var-file='variable's_file_name' & terraform apply–var-file='variable's_file_name'
on that point terraform will get different value from different file.
for demo
variable.tf (contain all variables)
variable "resource_group" {
type = string
description = "Resource Group"
default = ""
}
dev.tfvars (contain development variable's values)
resource_group="name_of_my_resource_group_dev"
prod.tfvars (contain production variable's values)
resource_group="name_of_my_resource_group_prod"
now when you run command terraform plan–var-file='dev.tfvars' it take the value from dev.tfvars file.
Related
I know inorder to have a remote state in my terraform code, i must create a storage account,and a container. Usually, it is done manually, but i am trying to create the storage account and the container dynamically using the below code:
resource "azurerm_resource_group" "state_resource_group" {
name = "RG-Terraform-on-Azure"
location = "West Europe"
}
terraform {
backend "azurerm" {
resource_group_name = "RG-Terraform-on-Azure"
storage_account_name = azurerm_storage_account.state_storage_account.name
container_name = azurerm_storage_container.state_container.name
key = "terraform.tfstate"
}
}
resource "azurerm_storage_account" "state_storage_account" {
name = random_string.storage_account_name.result
resource_group_name = azurerm_resource_group.state_resource_group.name
location = azurerm_resource_group.state_resource_group.location
account_tier = "Standard"
account_replication_type = "LRS"
tags = {
environment = "staging"
}
}
resource "azurerm_storage_container" "state_container" {
name = "vhds"
storage_account_name = azurerm_storage_account.state_storage_account.name
container_access_type = "private"
}
resource "random_string" "storage_account_name" {
length = 14
lower = true
numeric = false
upper = false
special = false
}
But, the above code complains that:
│ Error: Variables not allowed
│
│ on main.tf line 11, in terraform:
│ 11: storage_account_name = azurerm_storage_account.state_storage_account.name
│
│ Variables may not be used here.
So,I already know that the i cannot use variables in the backend block, however i am wondering if there is a solution which enable me to create the storage account and the container dynamically and store the state file in there ?
Point:
i have already seen this question, but the .conf file did not work for me!
This can't be done in the same Terraform file. The backend has to exist before anything else. Terraform requires the backend to exist when you run terraform init. The backend is accessed to read the state as the very first step Terraform performs when you do a plan or apply, before any resources are actually created.
In the past I've automated the creation of the storage backend using a CLI tool. If you wanted to automate it with terraform it would have to be in a separate Terraform workspace, but then where would the backend for that workspace be?
In general, it doesn't really work to create the backend in Terraform.
I'm using Terraform to manage my Infrastructure and Terratest for Testing.
In terraform, I have a module called ResourceGroup (located in the Modules folder)
resource "azurerm_resource_group" "azResourceGroup" {
name = var.resource_group_name
location = var.resource_region
tags = var.tags
}
output "resource_group_name" {
value = azurerm_resource_group.azResourceGroup.id
}
And I'm using this module in the main.tf to create an Azure Resource Group.
module "azResourceGroup" {
source = "./Modules/ResourceGroup"
resource_group_name = var.resource_group_name
tags = var.tags
}
output "resource_group_name" {
value = "${module.azResourceGroup.resource_group_name}"
}
When I execute terraform apply the output of main.tf is returning the full path of the newly created Resource Group instead of its ID.
Here's the Output
Changes to Outputs:
- resource_group_name = "/subscriptions/xxxxxxx-xxxx-xxxx-xxxx-x/resourceGroups/rg-svf-nprd-test" -> null
Preferably, it should return only rg-svf-nprd-test (Id from output variable).
Am I missing anything here? Please help me with this.
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
Here is a basic example for what I am trying to achieve. I have two files (main.tf) and (variable.tf), I want to create two resource groups and in the variables file is a list of names which I want the resource groups to occupy. First name of the first resource group and similarly going forward.
So help me out on how to achieve it. I am using terraform v0.13.
main.tf file:
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "test" {
count = 2
name = var.resource_group_name
location = var.location
}
variable.tf file:
variable "resource_group_name" {
description = "Default resource group name that the network will be created in."
type = list
default = ["asd-rg","asd2-rg"]
}
variable "location" {
description = "The location/region where the core network will be created.
default = "westus"
}
You just need to change the resource group block like this:
resource "azurerm_resource_group" "test" {
count = 2
name = element(var.resource_group_name, count.index)
location = var.location
}
You can use the for_each syntax to create multiple resource of similar type. It requires a set (of unique values) to iterate over, hence convert your variable resource_group_name to set.
variable.tf
variable "resource_group_name" {
description = "Default resource group name that the network will be created in."
type = list(string)
default = ["asd-rg","asd2-rg"]
}
variable "location" {
description = "The location/region where the core network will be created.
default = "westus"
}
main.tf
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "test" {
name = each.value // value from iteration
location = var.location
for_each = toset(var.resource_group_name) // convert list to set and iterate over it
}
Edit: variable can be of type string, list or map. This needs to be converted to set to be used with for_each
for you can use the combination of length and count to create multiple resources of the same type with unique and unidentical names.
You just need to change the resource group block like this:
resource "azurerm_resource_group" "test" {
count = length(var.resource_group_name)
name = element(concat(var.resource_group_name, [""]), count.index)
location = var.location
}
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.