azure resource prive endpoint creation error - azure

I am trying to create a private endpoint for Azure Function App using terraform
The code for functionApp is
resource "azurerm_resource_group" "example" {
name = "azure-functions-test-rg"
location = "West Europe"
}
resource "azurerm_storage_account" "example" {
name = "functionsapptestsa"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_app_service_plan" "example" {
name = "azure-functions-test-service-plan"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
sku {
tier = "PremiumContainer"
size = "P1"
}
}
resource "azurerm_function_app" "example" {
name = "test-azure-functions"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
app_service_plan_id = azurerm_app_service_plan.example.id
storage_account_name = azurerm_storage_account.example.name
storage_account_access_key = azurerm_storage_account.example.primary_access_key
}
This all works fine, the functionapp gets created. I am trying to create private endpoint to this functionapp with following code
resource "azurerm_private_endpoint" "examplepe" {
name = "example-endpoint"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
subnet_id = azurerm_subnet.endpoint.id #dummy data
private_service_connection {
name = "example-privateserviceconnection"
is_manual_connection = false
private_connection_resource_id = azurerm_function_app.example.id
subresource_names = ["blob"]
}
}
The error I am getting is " Error creating private endpoint "resource name".... failure sending request: Statuscode=0 -- Original Error: Code="BadRequest" Message="Call to Microsoft.Web/sites failed. Error message: GroupId is invalid." Details=[]
Thanks

The issue was with incorrect subResource name being selected.
Resource Type SubResource Name Secondary SubResource Name
Data Lake File System Gen2 dfs dfs_secondary
Sql Database / Data Warehouse sqlServer
Storage Account blob blob_secondary
Storage Account file file_secondary
Storage Account queue queue_secondary
Storage Account table table_secondary
Storage Account web web_secondary
Web App / Function App sites
Web App / Function App Slots sites-<slotName>
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint#subresource_names
This has the details of the subresource

Related

Trying to create azure storage account & use the same to store terraform state file

on the way to create aks via terraform, here i want to create azure storage account & want to use the same same account to store the terraform state file.
however getting below error
│ Error: Error loading state: Error retrieving keys for Storage Account "azurerm_resource_group.aks_rg.name": storage.AccountsClient#ListKeys: Invalid input: autorest/validation: validation failed: parameter=accountName constraint=MaxLength value="azurerm_resource_group.aks_rg.name" details: value length must be less than or equal to 24
│
#Create Resource Group
resource "azurerm_resource_group" "aks_rg" {
location = "${var.location}"
name = "${var.global-prefix}-${var.cluster-id}-${var.environment}-azwe-aks-rg"
}
#Create Storage Account & Container
resource "azurerm_storage_account" "storage_acc" {
name = "${var.cluster-id}-storage-account"
resource_group_name = azurerm_resource_group.aks_rg.name
location = azurerm_resource_group.aks_rg.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_storage_container" "storage_container" {
name = "${var.cluster-id}-storage-account-container"
storage_account_name = azurerm_storage_account.storage_acc.name
container_access_type = "private"
}
#store terraform state in remote container
terraform {
# Configure Terraform State Storage
backend "azurerm" {
resource_group_name = "azurerm_resource_group.aks_rg.name"
storage_account_name = "azurerm_storage_container.storage_acc.name"
container_name = "azurerm_storage_container.storage_container.name"
key = "terraform.tfstate"
}
}
You need to first create the storage account and container then while creating the aks cluster you need to give the below:
terraform {
# Configure Terraform State Storage
backend "azurerm" {
resource_group_name = "azurerm_resource_group.aks_rg.name"
storage_account_name = "azurerm_resource_group.aks_rg.name"
container_name = "powermeprodtfstate"
key = "terraform.tfstate"
}
}
Instead of creating the storage account and container ins the same file while storing the terraform tfstate.
Example:
Create storage account and container:
provider "azurerm" {
features {}
}
data "azurerm_resource_group" "example" {
name = "resourcegroupname"
}
resource "azurerm_storage_account" "example" {
name = "yourstorageaccountname"
resource_group_name = data.azurerm_resource_group.example.name
location = data.azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_storage_container" "example" {
name = "terraform"
storage_account_name = azurerm_storage_account.example.name
container_access_type = "private"
}
Then create the aks resource group and store the tfstate in container.
provider "azurerm" {
features {}
}
terraform {
# Configure Terraform State Storage
backend "azurerm" {
resource_group_name = "resourcegroup"
storage_account_name = "storageaccountnameearliercreated"
container_name = "terraform"
key = "terraform.tfstate"
}
}
resource "azurerm_resource_group" "aks_rg" {
name = "aks-rg"
location = "west us"
}
Reference:
How to store the Terraform state file in Azure Storage. » Jorge Bernhardt

Terraform 403 error when creating function app and storage account with private endpoint

I am getting a 403 forbidden when creating a function app that connects to its storage account via private endpoint inside a vnet. Storage account has firewall default action of 'Deny', and of course if I set it to 'Allow' it will work. I want this as 'Deny', however. Following this microsoft link if the function app and storage account are created in the same region with vnet, subnets, and private endpoints then it's supposed to work so I must be doing something wrong. I also tried changing the region for the storage account and it still resulted in a 403.
Error:
Error: web.AppsClient#CreateOrUpdate: Failure sending request: StatusCode=0 -- Original Error: Code="BadRequest" Message="There was a conflict. The remote server returned an error: (403) Forbidden." Details=[{"Message":"There was a conflict. The remote server returned an error: (403) Forbidden."},{"Code":"BadRequest"},{"ErrorEntity":{"Code":"BadRequest","ExtendedCode":"01020","Message":"There was a conflict. The remote server returned an error: (403) Forbidden.","MessageTemplate":"There was a conflict. {0}","Parameters":["The remote server returned an error: (403) Forbidden."]}}]
Here is my terraform code
resource "azurerm_function_app" "func" {
name = "${var.func_basics.name}-func"
location = var.func_basics.location
resource_group_name = var.func_basics.resource_group_name
app_service_plan_id = azurerm_app_service_plan.svc_plan.id
storage_account_name = azurerm_storage_account.func_sa.name
storage_account_access_key = azurerm_storage_account.func_sa.primary_access_key
version = var.runtime_version
https_only = true
depends_on = [
azurerm_storage_account.func_sa,
azurerm_app_service_plan.svc_plan,
azurerm_application_insights.func_ai,
azurerm_virtual_network.func_vnet
]
app_settings = merge(var.app_settings, local.additional_app_settings)
}
resource "azurerm_app_service_plan" "svc_plan" {
name = "${var.func_basics.name}-func-plan"
location = var.func_basics.location
resource_group_name = var.func_basics.resource_group_name
kind = "elastic"
sku {
tier = "ElasticPremium"
size = "EP1"
}
}
resource "azurerm_application_insights" "func_ai" {
name = "${var.func_basics.name}-func-appi"
location = var.func_basics.location
resource_group_name = var.func_basics.resource_group_name
application_type = var.ai_app_type
}
resource "azurerm_storage_account" "func_sa" {
name = "st${lower(replace(var.func_basics.name, "/[-_]*/", ""))}"
resource_group_name = var.func_basics.resource_group_name
location = var.func_basics.location
account_tier = var.sa_settings.tier
account_replication_type = var.sa_settings.replication_type
account_kind = "StorageV2"
enable_https_traffic_only = true
min_tls_version = "TLS1_2"
depends_on = [
azurerm_virtual_network.func_vnet
]
network_rules {
default_action = "Deny"
virtual_network_subnet_ids = [azurerm_subnet.func_endpoint_subnet.id]
bypass = [
"Metrics",
"Logging",
"AzureServices"
]
}
}
resource "azurerm_virtual_network" "func_vnet" {
name = "${var.func_basics.name}-func-vnet"
resource_group_name = var.func_basics.resource_group_name
location = var.func_basics.location
address_space = ["10.0.0.0/16"]
}
resource "azurerm_subnet" "func_service_subnet" {
name = "${var.func_basics.name}-func-svc-snet"
resource_group_name = var.func_basics.resource_group_name
virtual_network_name = azurerm_virtual_network.func_vnet.name
address_prefixes = ["10.0.1.0/24"]
enforce_private_link_service_network_policies = true
service_endpoints = ["Microsoft.Storage"]
delegation {
name = "${var.func_basics.name}-func-del"
service_delegation {
name = "Microsoft.Web/serverFarms"
actions = ["Microsoft.Network/virtualNetworks/subnets/action"]
}
}
}
resource "azurerm_subnet" "func_endpoint_subnet" {
name = "${var.func_basics.name}-func-end-snet"
resource_group_name = var.func_basics.resource_group_name
virtual_network_name = azurerm_virtual_network.func_vnet.name
address_prefixes = ["10.0.2.0/24"]
enforce_private_link_endpoint_network_policies = true
}
resource "azurerm_private_endpoint" "func_req_sa_blob_endpoint" {
name = "${var.func_basics.name}-func-req-sa-blob-end"
resource_group_name = var.func_basics.resource_group_name
location = var.func_basics.location
subnet_id = azurerm_subnet.func_endpoint_subnet.id
private_service_connection {
name = "${var.func_basics.name}-func-req-sa-blob-pscon"
private_connection_resource_id = azurerm_storage_account.func_sa.id
is_manual_connection = false
subresource_names = ["blob"]
}
}
resource "azurerm_private_endpoint" "func_req_sa_file_endpoint" {
name = "${var.func_basics.name}-func-req-sa-file-end"
resource_group_name = var.func_basics.resource_group_name
location = var.func_basics.location
subnet_id = azurerm_subnet.func_endpoint_subnet.id
private_service_connection {
name = "${var.func_basics.name}-func-req-sa-file-pscon"
private_connection_resource_id = azurerm_storage_account.func_sa.id
is_manual_connection = false
subresource_names = ["file"]
}
}
resource "azurerm_app_service_virtual_network_swift_connection" "func_vnet_swift" {
app_service_id = azurerm_function_app.func.id
subnet_id = azurerm_subnet.func_service_subnet.id
}
locals {
additional_app_settings = {
"APPINSIGHTS_INSTRUMENTATIONKEY" = azurerm_application_insights.func_ai.instrumentation_key
"WEBSITE_CONTENTAZUREFILECONNECTIONSTRING" = azurerm_storage_account.func_sa.primary_connection_string
"AzureWebJobsStorage" = azurerm_storage_account.func_sa.primary_connection_string
"WEBSITE_VNET_ROUTE_ALL" = "1"
"WEBSITE_CONTENTOVERVNET" = "1"
"WEBSITE_DNS_SERVER" = "168.63.129.16"
}
}
It seems that it's a common error message when you create an Azure function where the storage account of the function is added to the Virtual Network, read here for more details.
To resolve it, you can use the local-exec Provisioner to invoke the az CLI command to deny the traffic after all of the provisions are finished.
az storage account update --name storage_account_name --resource-group reource_group_name --default-action 'Deny' --bypass 'AzureServices', 'Logging', 'Metrics'
Alternatively, you can separately configure the storage account network rules. You may need to allow your client's IP to access the storage account.
resource "azurerm_storage_account_network_rules" "test" {
resource_group_name = var.resourceGroupName
storage_account_name = azurerm_storage_account.func_sa.name
default_action = "Deny"
bypass = [
"Metrics",
"Logging",
"AzureServices"
]
ip_rules = ["x.x.x.x"]
depends_on = [
azurerm_storage_account.func_sa,
azurerm_app_service_plan.svc_plan,
azurerm_application_insights.func_ai,
azurerm_virtual_network.func_vnet,
azurerm_function_app.func
]
}
In addition, there is a possible solution for this similar case on Github.
I've had this issue in the past and found that it can be resolved as follows. I've tested this on v3.3.0 of the provider using the azurerm_windows_function_app resource. I think currently this is an Azure problem, in that it if you don't supply a share it will try and create one but will be denied. You'd expect this to work if Allow Azure services on the trusted services list to access this storage account is enabled, but webapps aren't trusted.
Create your storage account with IP rules and deny
Create a share within this for your function app content
within the function set the following configuration settings
WEBSITE_CONTENTAZUREFILECONNECTIONSTRING = <storage_account.primary_connection_string>
WEBSITE_CONTENTSHARE = <your share>
WEBSITE_CONTENTOVERVNET = 1
In the functions site configuration set the attribute vnet_route_all_enabled = true

Deploy Azure VM based on customize Image available on Shared Image Gallery

I have first created a simple standard VM in Azure which I have customized for data science purpose with different software on it. Then I have created an image from this VM so that I can deploy new VM based on the customized image faster with same configuration. I have saved the image under Azure Shared Image Gallery.
Is it any way to deploy this customized image from a Terraform script into a new Resource Group? I know how to deploy a normal standard VM from Terraform but couldn't find out how to deploy it based on a customized image saved in the Shared Image gallery.
To deploy a customized image from Azure Shared Image Gallery with terraform. You could use Data Source: azurerm_shared_image and azurerm_windows_virtual_machine or azurerm_linux_virtual_machine to manage it with specify the source_image_id. Please note that the newly created VM should be in the same region as the shared image before you deploy it. If not, you could replicate this image to your desired region, read https://learn.microsoft.com/en-us/azure/virtual-machines/shared-image-galleries#replication
For example, deploy a Windows VM from generalized shared image:
provider "azurerm" {
features {}
}
data "azurerm_shared_image" "example" {
name = "my-image"
gallery_name = "my-image-gallery"
resource_group_name = "example-resources-imageRG"
}
resource "azurerm_resource_group" "example" {
name = "example-resources"
location = "xxxxximageregion"
}
resource "azurerm_virtual_network" "example" {
name = "example-network"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
}
resource "azurerm_subnet" "example" {
name = "internal"
resource_group_name = azurerm_resource_group.example.name
virtual_network_name = azurerm_virtual_network.example.name
address_prefixes = ["10.0.2.0/24"]
}
resource "azurerm_network_interface" "example" {
name = "example-nic"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
ip_configuration {
name = "internal"
subnet_id = azurerm_subnet.example.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_windows_virtual_machine" "example" {
name = "example-machine"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
size = "Standard_F2"
admin_username = "adminuser"
admin_password = "P#$$w0rd1234!"
network_interface_ids = [
azurerm_network_interface.example.id,
]
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_id = data.azurerm_shared_image.example.id
}
Result

azure function via terraform: how to connect to service bus

I am stuck when trying to deploy an Azure function via Azure DevOps pipelines and Terraform.
Running terraform apply works fine and the Service Bus looks good and works. In the Azure portal the function seems to be running, but it complains that it can not find the ServiceBusConnection.
I defined it via the following Terraform declaration:
resource "azurerm_resource_group" "rg" {
name = "rg-sb-westeurope"
location = "westeurope"
}
resource "azurerm_servicebus_namespace" "sb" {
name = "ns-sb"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
sku = "Standard"
}
resource "azurerm_servicebus_queue" "sbq" {
name = "servicebusqueue"
resource_group_name = azurerm_resource_group.rg.name
namespace_name = azurerm_servicebus_namespace.sb.name
enable_partitioning = true
}
resource "azurerm_servicebus_namespace_authorization_rule" "sb-ar" {
name = "servicebus_auth_rule"
namespace_name = azurerm_servicebus_namespace.sb.name
resource_group_name = azurerm_resource_group.rg.name
listen = false
send = true
manage = false
}
In the function app i declare:
resource "azurerm_function_app" "fa" {
name = "function-app"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
app_service_plan_id = azurerm_app_service_plan.asp.id
storage_account_name = azurerm_storage_account.sa.name
storage_account_access_key = azurerm_storage_account.sa.primary_access_key
app_settings = {
ServiceBusConnection = azurerm_servicebus_namespace_authorization_rule.sb-ar.name
}
}
This tf. will not work out of the box as i have not copied here the full declaration.
I think I am setting the connection environment vars wrong but have no idea on how to do it correctly.
EDIT
With the hint from #Heye I got it working. This is the correct snipped replacing the name with primary_connection_string.
resource "azurerm_function_app" "fa" {
name = "function-app"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
app_service_plan_id = azurerm_app_service_plan.asp.id
storage_account_name = azurerm_storage_account.sa.name
storage_account_access_key = azurerm_storage_account.sa.primary_access_key
app_settings = {
ServiceBusConnection = azurerm_servicebus_namespace_authorization_rule.sb-ar.primary_connection_string
}
}
You are setting the ServiceBusConnection value to the name of the authorization rule. However, you probably want to set it to the primary_connection_string, as that contains the key along with all the information needed to connect to the Service Bus.

Terraform can not create a storage account in Azure

I have a Terraform script that used to be able to create a storage account in Azure ok, but today started to return the error message:
azurerm_storage_account.testsa: 1 error(s) occurred:
* azurerm_storage_account.testsa: Error waiting for Azure Storage Account "terraformtesthubb" to be created: Future#WaitForCompletion: the number of retries has been exceeded: StatusCode=400 -- Original Error: Code="AadClientCredentialsGrantFailure" Message="Failure in AAD Client Credentials Grant Flow."
The trace logs don't show anything useful, and the term AadClientCredentialsGrantFailure literally returns nothing in Google. What is the cause?
Answering this one for myself because Google totally failed me.
This turned out to be an issue with Azure. Despite there being no errors listed in any of the status pages, the script would work in US West, but fail in US West 2.
After a few days this issue went away, so it was an intermittent Azure issue.
Edit
For reference, this was the script. Markers like #{Principal.TenantId} are being replaced during the template deployment.
provider "azurerm" {
client_id = "#{Principal.Client}"
client_secret = "#{Principal.Password}"
subscription_id = "#{Principal.SubscriptionNumber}"
tenant_id = "#{Principal.TenantId}"
}
resource "azurerm_resource_group" "testrg" {
name = "terraformtesthub#{Octopus.Environment.Name | ToLower}"
location = "#{Octopus.Environment.Name | ToLower}"
}
resource "azurerm_virtual_network" "test" {
name = "terraformtesthub#{Octopus.Environment.Name | ToLower}"
address_space = ["10.0.0.0/16"]
location = "${azurerm_resource_group.testrg.location}"
resource_group_name = "${azurerm_resource_group.testrg.name}"
}
resource "azurerm_subnet" "test" {
name = "terraformtesthub#{Octopus.Environment.Name | ToLower}"
resource_group_name = "${azurerm_resource_group.testrg.name}"
virtual_network_name = "${azurerm_virtual_network.test.name}"
address_prefix = "10.0.2.0/24"
service_endpoints = ["Microsoft.Sql", "Microsoft.Storage"]
}
resource "azurerm_storage_account" "testsa" {
name = "terraformtesthub#{Octopus.Environment.Name | ToLower}"
resource_group_name = "${azurerm_resource_group.testrg.name}"
location = "#{Octopus.Environment.Name | ToLower}"
account_tier = "Standard"
account_kind = "StorageV2"
account_replication_type = "RAGRS"
lifecycle {
prevent_destroy = true
}
network_rules {
ip_rules = ["100.0.0.1"]
virtual_network_subnet_ids = ["${azurerm_subnet.test.id}"]
}
}

Resources