Is it possible to add a new VM only using Terraform? All the examples/samples and everything I have used Terraform for so far has me adding the VNet, Subnet, Network Interface, VM, Storage, etc., etc. all at the same time, referencing the resources crated within the script when creating other resources. For example, Terraform the Network Interface and then reference that when creating the VM.
What about if you already have the VNet, Subnet, etc. and just want to add a new, for example, Network Interface. Every time I try to do this and just reference what I think is the correct id, the plan stage works but then the apply fails with an "autorest:DoErrorUnlessStatusCode 400" error on he PUT call.
Is it just not possible to do this unless the resources were originally created using Terraform?
Yes you can. You can get the id from an created subnet with output. Like:
output "subnetid" {
value = "${azurerm_subnet.xxx.id}"
}
In your next template you can use this id in the subnet_id field.
The value of "${azurerm_subnet.xxx.id}" is base on the resourcegroup/vnet/subnet. So if you know how it is build you can also like to resource that are not created in terraform.
You can use the data directive to get existing resources as below. You can then interpolate these to create your VM.
data "azurerm_resource_group" "existing_deploy_rg" {
name = "RG"
}
data "azurerm_virtual_network" "existing_vnet" {
name = "existing-vnet"
resource_group_name = "${data.azurerm_resource_group.existing_deploy_rg.name}"
}
data "azurerm_subnet" "existing_subnet" {
name = "existing-subnet"
resource_group_name = "${data.azurerm_resource_group.existing_deploy_rg.name}"
virtual_network_name = "${data.azurerm_virtual_network.existing_vnet.name}"
}
Related
New to Terraform and as the question states I want to add additional APIs to an existing API Manager instance. Below is the example Terraform and this works fine. But I want to add a second api. Is it absolutely required that I put it inline in this Terraform script? The reason I ask is, If my team develops "example api" and a second team develops "awesomeexample api" can they have the Terraform code they need just for the api resource in their repository? Do I need to get the name of the API Manager through Powershell or GraphAPI? Edit: Right now when I add an additional api resource it destroys the first. I am guessing this is because of Terraform state...but that is not the behavior I want. I just want to keep adding apis or altering them independently of the api manager.
resource "azurerm_resource_group" "example" {
name = "example-resources"
location = "West Europe"
}
resource "azurerm_api_management" "example" {
name = "example-apim"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
publisher_name = "My Company"
publisher_email = "company#terraform.io"
sku_name = "Developer_1"
}
resource "azurerm_api_management_api" "example" {
name = "example-api"
resource_group_name = azurerm_resource_group.example.name
api_management_name = azurerm_api_management.example.name
revision = "1"
display_name = "Example API"
path = "example"
protocols = ["https"]
import {
content_format = "swagger-link-json"
content_value = "http://conferenceapi.azurewebsites.net/?format=json"
}
}
Me, I've been putting each API in it's own tf file.
Like for your example api, I'd create a api-example.tf file, cut/paste the azurerm_api_management_api block into it.
terraform init/plan/apply should still just work.
To add a new API into this API Mgmt instance, just copy api-example.tf to api-newapi.tf, edit as appropriate (name/displayname/path/import, and poof, new api.
I don't like using the same instance name for multiple different resources. here you used example three times, an RG, APIM, and APIM_API. it gets confusing and may break the minute you add a new API also referenced as 'example'. I use thisrg, ex-apim, and example-api (since your original API is called example). Your awsomeexample api I'd call awesome-api or something like that to differentiate example-api vs awesomeexample-api
Just ran into a similar thing that led me here.
One of our developers had added his resource by modifying our terraform files, planned it and applied it. By doing that he modified the remote state, but he never commited his changes. Thus when we were going to apply the terraform files from master, the locally planned state did not match what was in the remote.
In your case though, it is hard to tell. If you define two APIs in the same file that collide (e.g. 2x resource "azurerm_api_management_api" "example") that might cause you issues.
I'm setting up a virtual network in Azure with Terraform.
I have several VNets each with their own Network Security Group 100% managed in Terraform, no resources except the Resource Group exist prior to running Terraform.
When I run Terraform apply the first time all the resources are created correctly. However if I try and run apply again to update other resources I get an error saying the NSG resources already exist.
Error: A resource with the ID
"/subscriptions/0000000000000000/resourceGroups/SynthArtInfra/providers/Microsoft.Network/networkSecurityGroups/SynthArtInfra_ServerPoolNSG"
already exists - to be managed via Terraform this resource needs to be
imported into the State. Please see the resource documentation for
"azurerm_network_security_group" for more information.
Why is Terraform complaining about an existing resource when it should already be under it's control?
Edit:
This is the code related to the NSG, everything else is to do with a VPN gatway:
# Configure the Azure provider
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.26"
}
}
}
provider "azurerm" {
features {}
}
data "azurerm_resource_group" "SynthArtInfra" {
name = "SynthArtInfra"
location = "Somewhere" # not real
most_recent = true
}
resource "azurerm_virtual_network" "SynthArtInfra_ComputePool" {
name = "SynthArtInfra_ComputePool"
location = azurerm_resource_group.SynthArtInfra.location
resource_group_name = azurerm_resource_group.SynthArtInfra.name
address_space = ["10.6.0.0/16"]
}
resource "azurerm_subnet" "ComputePool_default" {
name = "ComputePool_default"
resource_group_name = azurerm_resource_group.SynthArtInfra.name
virtual_network_name = azurerm_virtual_network.SynthArtInfra_ComputePool.name
address_prefixes = ["10.6.0.0/24"]
}
resource "azurerm_network_security_group" "SynthArtInfra_ComputePoolNSG" {
name = "SynthArtInfra_ComputePoolNSG"
location = azurerm_resource_group.SynthArtInfra.location
resource_group_name = azurerm_resource_group.SynthArtInfra.name
security_rule {
name = "CustomSSH"
priority = 119
direction = "Inbound"
access = "Allow"
protocol = "*"
source_port_range = "*"
destination_port_range = "0000" # not the real port number
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
The other odd thing is our subscription has a security policy that automatically adds NSGs to resources that don't have one. But weirdly after applying my terraform script the NSGs are created but aren't actually associated with the Subnets and the security policy has created new NSGs. This needs to be resolved but didn't think it would cause this error.
I think what was going on is this is my first time using Terraform so I was getting a lot of errors midway through apply and destroy operations.
I ended up manually removing all the resources in Azure and deleting Terraform's local cache then everything started working.
TLDR;
Try removing any custom dependencies between resources that you have added yourself.
Hi, I came across this post whilst having a similar problem and will put my solution here in case that helps someone else.
I was working on creating a Cloud Run Service through Terraform. The first time went great and it created the resource I wanted, but as soon as I ran the apply again I would get this error saying that a resource with that name already exists. This was strange because according to the plan it was supposed to delete and then replace that resource.
What happened was that I added an uneccessary depends_on field on a few other resources and this was blocking the Cloud Run Service resource from being deleted before trying to create a new one.
According to the docs the depends_on field is only needed if there is some strange dependency that cannot be inferred by looking at the fields. So I just removed all of the custom ties between the dependencies and can now re-apply as much as I like.
Recently i figured out that my AKS cluster holds a subnet which is too small. Therefor im trying to add a second subnet and nodepool which is possible with the Azure CNI nowadays and then create a single proper subnet instead and migrate it back.
During terraform plan all goes well with a valid response however while applying it throws an error.
Error: Error Creating/Updating Subnet "me-test-k8s-subnet2" (Virtual Network "me-test-k8s-vnet" / Resource Group "me-test-k8s-rg"): network.SubnetsClient#CreateOrUpdate: Failure sending request: StatusCode=400 -- Original Error: Code="NetcfgInvalidSubnet" Message="Subnet 'me-test-k8s-subnet2' is not valid in virtual network 'me-test-k8s-vnet'." Details=[]
on main.tf line 28, in resource "azurerm_subnet" "subnet2":
28: resource "azurerm_subnet" "subnet2" {
My original cluster is created with this configuration of Terraform:
name = "${var.cluster_name}-rg"
location = "${var.location}"
}
resource "azurerm_virtual_network" "network" {
name = "${var.cluster_name}-vnet"
location = "${azurerm_resource_group.rg.location}"
resource_group_name = "${azurerm_resource_group.rg.name}"
address_space = ["10.1.0.0/16"]
}
resource "azurerm_subnet" "subnet" {
name = "${var.cluster_name}-subnet"
resource_group_name = "${azurerm_resource_group.rg.name}"
address_prefixes = ["10.1.0.0/24"]
virtual_network_name = "${azurerm_virtual_network.network.name}"
}
To make things more easy i decided to first add the subnet to the network without the nodepool. This will bring me to this terraform plan:
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# azurerm_subnet.subnet2 will be created
+ resource "azurerm_subnet" "subnet2" {
+ address_prefix = (known after apply)
+ address_prefixes = [
+ "10.2.0.0/22",
]
+ enforce_private_link_endpoint_network_policies = false
+ enforce_private_link_service_network_policies = false
+ id = (known after apply)
+ name = "me-test-k8s-subnet2"
+ resource_group_name = "me-test-k8s-rg"
+ virtual_network_name = "me-test-k8s-vnet"
}
Hope that someone can explain me why this error occurs.
Best,
Pim
When creating a subnet in a virtual network, it is mandatory to check if it is not jumping out of the network range.
You are just out of the range with your network mask: 10.1.0.0/16
First host: 10.1.0.1
Last host: 10.1.255.254
And you are trying to create subnet 10.2.0.0/22.
For not overlapping with subnets that are already created, 10.1.4.0/22, can be accepted, for instance.
As mentioned in my comment and in someone's answer, Azure is throwing this error because you are trying to add a 10.2.0.0/22 subnet to a 10.1.0.0/16 network. ie- 10.2.0.0/22 is not part of that network.
I also want to point out that when you run a plan that is not submitting the actual API calls to Azure to make the changes, which is why things looked fine to you when you ran your plan, but Azure complained when you tried to apply it. I think the explanation is good in this tutorial. The excerpts that are applicable are:
Once you are happy with your declared configuration, you can ask
Terraform to generate an execution plan for it. The plan command in
the CLI is used to generate an execution plan from a configuration.
The execution plan tells you what changes Terraform would need to make
to bring your current infrastructure to the declared state in your
configuration.
If you accept the plan you can instruct Terraform to apply changes. Terraform will make the API calls required to implement the changes. If anything goes wrong terraform will not attempt to automatically rollback the infrastructure to the state it was in before running apply. This is because apply adheres to the plan
You might also run to a similar error if you try to deploy another vnet into a subscription where there already is a vnet with the same address space.
I want to whitelist the ip addresses of an App Service Plan on a managed Sql Server.
The problem is, the resource azurerm_app_service_plan exposes its ip addresses as a comma-separated value, on the attribute possible_outbound_ip_addresses.
I need to create one azurerm_sql_firewall_rule for each of these ips.
If I try the following approach, Terraform gives an exception:
locals {
staging_app_service_ip = {
for v in split(",", azurerm_function_app.prs.possible_outbound_ip_addresses) : v => v
}
}
resource "azurerm_sql_firewall_rule" "example" {
for_each = local.staging_app_service_ip
name = "my_rules_${each.value}"
resource_group_name = data.azurerm_resource_group.example.name
server_name = var.MY_SERVER_NAME
start_ip_address = each.value
end_ip_address = each.value
}
I get then the error:
The "for_each" 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 for_each depends on.
I'm not sure how to work around this.
For the time being, I have added the ip addresses as a variable, and am manually setting the value of the variable.
What would be the correct approach to create these firewall rules?
I'm trying to deal with the same issue. My way around it is to perform multi-step setup.
In the first step I run terraform configuration where it creates database, app service, api management and some other resources. Next I deploy the app. Lastly I run terraform again, but this time the second configuration creates sql firewall rules and api management api from deployed app swagger definition.
I am trying to set up an EC2 with elastic IP with terraform. I am trying to use the existing VPC and subnets for the new EC2.
But Terraform is unable to recognise the existing subnet.
I am using the pre existing subnet like this -
variable "subnet_id" {}
data "aws_subnet" "my-subnet" {
id = "${var.subnet_id}"
}
When I run terraform plan I get this error -
Error: InvalidSubnetID.NotFound: The subnet ID 'subnet-02xxxxxxxxxx7' does not exist
status code: 400, request id: c4b6142b-5dfd-458c-959d-e5440b89c9fd
on ec2.tf line 3, in data "aws_subnet" "my-subnet":
3: data "aws_subnet" "my-subnet" {
This subnet was created by terraform in the past. So why does it say it doesn't exist?
Suggested debug:
Create 2 new terraform files:
First file, create a simple subnet (or VPC then subnet whatever)
Second file, try to retreive the subnet id like you posted.
The idea here is not to change anything else, meaning, same region, same creds, same everything.
Possible outputs:
1) you're not able to get the subnet ID - then you should be looking at things like the terraform version, provider version, stuff like that
2) you get the subnet ID, which means something in your creds, region, copy&paste of the name, basically human error is leading this blockade and you should revisit how you're doing things with emphysis on typos and permissions.
We can use the Data-Sources aws_vpc, aws_subnet and aws_subnet_ids:
data "aws_vpc" "default" {
default = true
}
data "aws_subnet_ids" "selected" {
vpc_id = data.aws_vpc.default.id
}
data "aws_subnet" "selected" {
for_each = data.aws_subnet_ids.selected.ids
id = each.value
}
And we can use them like in this LB example below:
resource "aws_alb" "alb" {
...
subnets = [for subnet in data.aws_subnet.selected : subnet.id]
...
}
This provides a reference to the VPC and subnets so you can pass the ID to other resources. Terraform does not manage the VPC or subnets when you do this, it simply references them.
It's irrelevant whether the subnet was initially created by Terraform or not.
The data source is attempting to find the subnet in the current state file. Your plan command returns the error because the subnet is not in your state file.
Try importing the subnet and then re-running the plan.
$ terraform import aws_subnet.public_subnet subnet-02xxxxxxxxxx7