Error while provisioning Terraform subnet using azurerm - azure

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.

Related

Using Terraform to upload Azure APIM

I'm having a bit of problem parsing the differences between Azure's name for things and Terraform's name for things, but overall I'm making a good go of it. I am having some specific problems, though. My situation is that someone built the APIM using the Azure portal, and the company now wants to "make it scalable" by using Terraform to build it out. I've got a pretty good riff going - define, plan, import, plan, modify - but there are some parts of Azure APIM that can't map (mentally) to Terraform commands. My first one is this screen right here (the definitions tab of an API in APIM:)
Since I'm still fresh in terms of rep on Stack, I can't actually show the image. But in the portal at the bottom of the API there is a tab called "definitions". I haven't been able to see a) how to "get" them using Azure Powershell, and b) I how to "set" them with Terraform.
Would someone more knowledgeable about AzureRM and Terraform be able to steer me in the right direction please?
One of the workaround you can follow to deploy an API management instance with api's.
We have tried to create APIM instance with API,
Here is the sample terraform code that we used you can use it by adding resource name according to your requirement.
example.tf:-
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "example" {
name = "example-resources"
location = "West Europe"
}
resource "azurerm_api_management" "example" {
name = "example-apimajmt"
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-apiajmt"
resource_group_name = azurerm_resource_group.example.name
api_management_name = azurerm_api_management.example.name
revision = "1"
display_name = "ajtest API"
path = "example"
protocols = ["https"]
import {
content_format = "swagger-link-json"
content_value = "http://conferenceapi.azurewebsites.net/?format=json"
}
}
After creation we can use it for adding tags
/* resource "azurerm_api_management_api_tag" "example" {
api_id = azurerm_api_management_api.example.id
name = "example-tagajmt"
}*/
Once the terraform apply is done then you will able to get the APIM instance along with the API and their tags after sometimes.
NOTE:- Creation of APIM will take upto 45 minutes.
OUTPUT SCREENSHOT FOR REFERENCE:-
For more information with configuration in APIM management by terraform please refer to this HashiCorp| Terraform Registry azurerm_api_management & this Similar SO THREAD|Tag an API in Azure API Management with Terraform.

Getting Terraform resource already exists error with resource just created by Terraform

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.

Create azurerm_sql_firewall_rule for an azurerm_app_service_plan in Terraform

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.

Terraform unable to recognise pre existing Subnet in AWS

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

Add ARM Azure VM only via Terraform

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

Resources