Azure ARM template nested template deployment won't update resources\fails to start - azure

I have the following ARM Template structure:
Parent Template
|--Nested Template 1
|--...
|--Nested Template 6
So I only have 2 levels of templates, Parent and nested.
Lets say I deploy parent to an empty resource group and everything works well. After that I delete one of the resources and want to deploy the same Parent Template with the same parameters to bring deleted resources back. But the deployment would fail saying that the resource already exists (the other, not the one i'm tried to recreate). I tried both incremental mode and full mode for deployments.
If i directly invoke nested template with the missing resources it works as expected (so specifically creating a deployment with nested template only, not with parent that invokes nested template).
UPD:
After some additional testing I can conclude thats even weirder then before. So I'm starting this deployment with powershell:
New-AzureRmResourceGroupDeployment #parameters
And it deploys just fine, however if I invoke the same command after the first deployment completed I would get an error:
The resource 'gggg-1s-the-wordd' already exists in location
'westeurope' in resource group 'gggg'. A resource with the same name
cannot be created in location 'northeurope'. Please select a new
resource name.
Is this behavior excepted? I can't seem to find anything relevant, thanks!
UPD2: It doesn't really matter if I use portal or powershell, I get the same error.

So with the help from Brian we were able to identify the culprit. The issue was that the WebApp had its location set to resourcegroup().location while the App Service Plan was correctly getting location from parameters. So that lead to a problem where at deployment time WebApp would deploy to the region where its App Service Plan was, but at evaluation time it would consider that this WebApp belongs to the region where the resource group was.
TLDR - copy paste error, which coupled with a bug in evaluation of location in ARM lead to a quite weird behavior.

If you deploy the same resource (intentionally did not use the word "template" there) to the same resource group, Azure should "make it so". IOW, if it's not there, it will create it, if it is there, it should no-op. It's not that black and white there are some nuances (like you can't change certain properties if the resource exsists) but if you deploy the same resource with the same property values to the same resource group you should not get an error.
In general, nesting (or not) shouldn't affect any of this.
If you're deploying to different resource groups, then you could see an error about "already exists" depending on the resource.
All that said, it's really hard to tell in your specific case what's going on without more detail... So if this doesn't help, can you add some detail (what's the exact error message) or a repro (template that we could see the problem with)?

I experienced the same issue. The reason was that, location of App Service was defined as [resourceGroup().location] instead of App service plan (ASP) location, which was creating the problem. I changed it by passing the location of ASP as a parameter to the template.
Getting location of of ASP is as:
internal static string GetASPLocation(TokenCloudCredentials credentials, string resourceGroup, string ASP)
{
Console.WriteLine($"Getting location of App Service Plan {ASP} in Resource Group {resourceGroup}");
var resourceClient = new ResourceManagementClient(credentials);
ResourceExistsResult result = resourceClient.Resources.CheckExistence(resourceGroup, new ResourceIdentity(ASP, "Microsoft.Web/serverfarms", "2015-08-01"));
var appServicePlan = resourceClient.Resources.Get(resourceGroup, new ResourceIdentity(ASP, "Microsoft.Web/serverfarms", "2015-08-01"));
return appServicePlan.Resource.Location;
}
And in ARM template, location can be changed as :
"location": "[parameters('ASPLocation')]"

Related

Best bicep structure with CI/CD pipeline

I'm not sure that there is a right answer for this and it will vary per scenario but I am curious because there isn't much documentation that I can find for a code first azure bicep infrastructure. Most examples you find show how to make a resource within a resource group, or using a module to define scope and deploy to another resource group, but what if you're trying to do more?
Let's do the following scenario: using 2 subscriptions(1 for prod, 1 for dev & qa) with 20 resource groups each containing multiple difference resources and you want to manage this within a CI/CD pipeline, plus the 3 environments: prod, qa, and dev. How would you go about this? I can think of a few scenarios but don't necessarily, but nothing sticks out as the best way to do it, maybe I'm missing something.
CI/CD portion:
Let's assume:
az account set --subscription(set our sub)
az group create --name --location (create resource group if it doesn't exist)
az deployment group create --name --resource-group --template-file --parameters(read from our files to deploy to a resource group)
You could pass an array of resource groups to loop through to create the resource group if it doesn't exist.
You could have the resource group list in a parameters file that you read from and do the same thing as above.
You could create a step for every resource group and the resources inside of it.(seems excessive?)
Bicep Portion:
Bicep restrictions: to specify scope(a resource group in our scenario) we'd have to have use modules dealing with multiple resource groups or have a step for each resource group and have a main.bicep file for the different resource groups/resources.
You could create a folder structure for each resource group and the resources inside of it with a main.bicep but that would mean you have a lot of extra deploy steps(seems excessive?).
You could have 1 main.bicep file and have a folder structure that uses a lot of modules to specify your scope while reading the resource group, resource variables etc using an environment parameters.json file.
You could create a folder for each environment, have folders with each environment then create each resource group and resources inside of it not using a parameters.json but using params in each file instead since they would be specific for each environment.
1 final issue:
Lastly let's say you want to add a step before the deployment of resources to use bicep what-if to check what resources will be updated or deleted(this is pretty important!). Last I checked there was an issue where what-if does not work for bicep modules so you wouldn't get the luxury of knowing what changes would be made prior to a deployment with the what-if. That is a pretty big safety net you'd be losing, so would you want to scratch the module strategy all together?
What would be the best way to tackle something like this while keeping it readable for average non experts to be able to hop in and work on it? I would lean towards making a folder structure using modules and reading from an environment parameters.json but I'm not convinced that's the best way, especially if what-if isn't fully working for bicep modules.
IMO this does depend a lot on the scenario, topology, permissions, etc. The way I would start thinking about this is that you want an "environment" that will vary a bit between dev/test and prod. That env has multiple resourceGroups and a dedicated subscription for each env.
In this case, I would use a single bicep "project" (e.g. main.bicep with modules) and change the deployment using parameter files (for dev/test vs. prod). The project would lay down everything needed for the environment (think greenfield). The main.bicep file is a subscription scoped deployment that will create the RGs and all the resources needed. Oversimplified example:
targetScope = 'subscription'
param sqlAdminUsername string'
param keyVaultResourceGroup
param keyVaultName string
param keyVaultSecretName string
param location string = deployment().location
resource kv 'Microsoft.KeyVault/vaults#2021-06-01-preview' existing = {
scope: resourceGroup(subscription().subscriptionId, keyVaultResourceGroup)
name: keyVaultName
}
resource sqlResourceGroup 'Microsoft.Resources/resourceGroups#2021-04-01' = {
name: 'shared-sql'
location: location
}
resource webResourceGroup 'Microsoft.Resources/resourceGroups#2021-04-01' = {
name: 'shared-web'
location: location
}
module sqlDeployment 'modules/shared-sql.bicep' = {
scope: resourceGroup(sqlResourceGroup.name)
name: 'sqlDeployment'
params: {
sqlAdminUsername: sqlAdminUsername
sqlAdminPassword: kv.getSecret(keyVaultSecretName)
location: location
}
}
module webDeployment 'modules/shared-web.bicep' = {
scope: resourceGroup(webResourceGroup.name)
name: 'webDeployment'
params: {
location: location
}
}
A single template + modules that creates the RGs, creates a SQL Server (via module) and an app service plan with an admin website (also via module). You can then parameterize whatever you want to for each environment.
re: what-if - what if will skip evaluation of a module if that module has a parameter that is an output of another module. If you don't pass outputs between modules then the module will be evaluated by what-if. The sample above does not pass outputs - often you don't need to do this because the information output was known by the parent (i.e. main.bicep) but sometimes you can't avoid it - ymmv.
Once you have the template designed in such a way, the pipeline is really straightforward - just deploy the template to the desired subscription.
That help?

Issue when creating Logic App through Terraform

I'm trying to create a Logic App through Terraform and facing issue related to API Connection.
Here are the manual steps for creating the API Connection:
Create a Logic App in your resource group and go to Logic App Designer
Select the HTTP trigger request and click on "Next Step", then search and select "Azure Container Instance"
Click on Create or update container group and it should ask you to sign-in
Now if you scroll all the way down, you should see "Connected to ...... Change Connection"
If the Change Connection is clicked, it will show the existing aci connections or create a new one.
I'm trying to create a Logic App and I'm facing an issue with the above mentioned steps.
What I'm doing is:
Exported the existing Logic App template from another environment
Converted the values in the json as parameters and kept them in variables.tf and the final values in terraform.tfvars
The terraform plan is working fine, however the terraform apply is causing an issue
Error message:
Error: waiting for creation of Template Deployment "logicapp_arm_template" (Resource Group "resource_group_name"): Code="DeploymentFailed" Message="At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details." Details=[{"code":"NotFound","message":"{\r\n \"error\": {\r\n \"code\": \"ApiConnectionNotFound\",\r\n \"message\": \"**The API connection 'aci' could not be found**.\"\r\n }\r\n}"}]
Further troubleshooting shows that the error occurs in this line in terraform.tfvars
connections_aci_externalid = "/subscriptions/<subscription_id>/resourceGroups/<resource_group_name>/providers/Microsoft.Web/connections/aci"
Deduced that the issue is since the "aci" is not created.
So, created the aci manually through the Azure Portal (see top of the post for steps).
However, when I hit terraform apply the new error below shows up:
A resource with the ID "/subscriptions/<subscription_id>/resourceGroups/<resource_group_name>/providers/Microsoft.Resources/deployments/logicapp_arm_template" already exists - to be managed via Terraform this resource needs to be imported into the State. Please see the resource documentation for "azurerm_resource_group_template_deployment" for more information.
My question is, since I'm creating the Logic App using the existing template how should the "aci" portion be handled through Terraform?
For your last error message, you could remove the terraform.tfstate and terraform.tfstate.backup files in the terraform working directory and existing resources in the Azure portal then run terraform plan and terraform apply again.
If you have a separate working ARM Template, you can invoke the template deployment with terraform resource azurerm_resource_group_template_deployment. You could provide the contents of the ARM Template parameters file with argument parameters_content and the contents of the ARM Template file with argument template_content.
In this case, If you have manually created a new API Connection, you can directly input your new API connection id /subscriptions/<subscription_id>/resourceGroups/<resourceGroup_id>/providers/Microsoft.Web/connections/aci. Alternatively, you can create the API Connections automatically when you deploy your ARM Template with resource Microsoft.Web/connections. Read this blog for more samples.
If using the azurerm_resource_group_template_deployment, make sure that deployment mode is set to incremental, otherwise, you run into terrible state issues. An example from our terraform module can be seen below. We use this to deploy an arm template, which we design in our development environment and export from the Azure portal. This enables us to use parameters to deploy exactly the same logic app in test, acceptance, and production environments.
resource "azurerm_logic_app_workflow" "workflow" {
name = var.logic_app_name
location = var.location
resource_group_name = var.resource_group_name
}
resource "azurerm_resource_group_template_deployment" "workflow_deployment" {
count = var.arm_template_path == null ? 0 : 1
name = "${var.logic_app_name}-deployment"
resource_group_name = var.resource_group_name
deployment_mode = "Incremental"
template_content = file(var.arm_template_path)
parameters_content = jsonencode(local.parameters_content)
depends_on = [azurerm_logic_app_workflow.workflow]
}
Note the conditional using count. Setting arm_template_path = null by default enables us to deploy only the workflow "container" in our development environment. Which can then be used as a "canvas" for designing the logic app.

What is the correct, programmatic way to identify if an Azure region supports a resource type fully in Powershell?

Currently, I'm looking at using Front Door in an Azure resource group. When I look at https://azure.microsoft.com/en-gb/global-infrastructure/services/?products=frontdoor&regions=all , it shows that this resource is in GA.
However, if I attempt to deploy a working, tested ARM template to certain regions I get an error which explictly indicates that the region is unsupported (this error message is from an Azure Devops pipeline, but I have seen the same error using powershell deployment ofthe same template with a params file locally):
As such, it doesn't seem like I can trust the availability by region site (and to be fair, it does have a disclaimer on it).
How do I tell, before a deployment is fired, whether I can safely use (i.e. deploy a specified resource type to) the region?
Ideally I'm looking for a dynamic solution to this, as just because a region isn't supported now doesn't mean it won't be in the future.
For your use-((Register-AzResourceProvider -ProviderNamespace Microsoft.Network).ResourceTypes | where-object ResourceTypeName -eq frontdoors).Locations
which will provide list of locations
You can refer following link for more examples-
https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-supported-services#azure-powershell

Azure Data Factory Release Pipeline - Resource Not Found Error

I am testing a build/release of a very simple ADF (just one activity), the build, repository, arm template export all seem ok until when I run the release task, the error shows up in the final step, that says:
"error": {
"code": "ResourceNotFound",
"message": "The Resource 'Microsoft.DataFactory/factories/htTestDF' under resource group 'xxx-rg' was not found."
}
I watched several tutorials and microsoft web sites, and tried exporting the ARM template several times, the same error occurs. Any ideas will be greatly appreciated.
Thanks for your details clarifies in comments. Now, the error message you met caused by using a different target resource group as this ARM template deploy to.
To make this more clear, I reproduce the issue based on the details you provided. Fortunately, got the same error with you. Now, let's focus on its log, then get why it cause the Not Found error.(Please set debug=true)
As I mentioned in the above pic, it is the api that this task used at first step while the template begin to apply into the corresponding resource group and deployment. For more cleared, please refer to this REST API doc firstly: Deployments - Create Or Update.
The logic of this task is compile parameters from ARM template file, pack them and use it as request body for this PUT api call. See its api doc, you can get that for this API call, its resourceGroupName and deploymentName need to be specified firstly. In another words, if you specified another target resource group, it would not find the correct target place that can apply this template definition. Because, you can see that this ARM template is preparing to applying activities SetVariable into your Data Factory HTDF3 and the defined pipelines name is HTPipe1. But these should all not exists in your target Resource Group. Thus it caused the error like this:
"error": {
"code": "ResourceNotFound",
"message": "The Resource 'Microsoft.DataFactory/factories/Merlin-1003' under resource group 'Merlin-ARM-deploy' was not found."
}
In my sample, Merlin-ARM-deploy is my target resource group.
If you want to deploy this into your target resource group, you need to create one data factory manually, or use another ARM Template to create a new one same factory in the target resource group. If you choose the previous method, just then modify the template.json file, to let its parameters correspond to the actual target resource group. But if use the second method, do not operate anything. Just apply them with task.
The ARM template generated by ADF(publish) cannot be deployed directly to a new RG.
Solution
Create RG(optional, assuming it is IAC(infra as code))
Run a powershell script task in pipeline to create an empty ADF(do not use empty ARM instead). Since it is not ARM you would need to put an optional condition to check if it already exists)
Set-AzDataFactoryV2 -ResourceGroupName "RG" -Name "ADF" -Location "North Europe"
Now we can execute the ARM template from publish folder (the one you had given)
Errors in ADF publish system.
The ARM template need to be generated in such a way that it is
idempotent(should also create afresh if not present). But it is not
at the moment. It expects an ADF to be present already(strangely).
When an empty ARM template created in another RG is used to create a fresh empty ADF in this RG(newly created), it fails. Well, it creates empty ADF but we cannot put adf_publish(default publish folder for ADF) on top of it because we get 'resource not found error'.
But when we manually create an ADF and run the adf_publish template
then it works! But ofcourse, this is not what we want.
Why does manual & powershell work(empty + publish) but not ARM template? It could be that the ARM Template has wrong location/region mentioned in it but that was not the case.(really puzzling to me)

Deploy a resource group with App Service

I have published an API app from Visual Studio to a new resource group, also created an App Service Plan, so after the deployment the resource group contains 2 items:
AppServicePlan1
AppService1
Now I am trying to deploy these items to another resource group as follows:
Select 'Automation script' in the resource group settings
Click 'Deploy'
Select 'Create new' resource group, enter its name
Enter 'Serverfarms_AppServicePlan1_name' parameter value (new app service plan name, e.g. AppServicePlan2)
Enter 'Sites_AppService1_name' parameter value (new app service name, e.g. AppService2)
Tick 'Agree to terms and conditions'
Click 'Puchase'
The process fails with the following error message:
"The host name AppService1.azurewebsites.net is already assigned to another Azure website: AppService1."
I have tried different things - deploying app service plan, logic apps, etc. from one resource group to another works fine, but deploying an app service fails as described above.
I have tried changing the hostNames property in the template file to ["AppService2.azurewebsites.net"] manually - I'm not getting the error then (although I'm not sure, maybe something else also needs to be changed, e.g. properties enabledHostNames, hostNameSslStates?) and the deployment seems to work, however the 'deployed' app service can't be used as it contains only 1 file - hostingstart.html.
What am I missing?
I think you have everything correct - as you noticed when you use the generated automation script it will create everything with the same properties that currently exist. We try to parameterize the correct value (like the web app name) but there are some details that can be overlooked (like host names). After changing all that it sounds like you got it to deploy.
The "code" however is not part of the automation script - only the infrastructure and configuration. So you still need to deploy your app to have it be identical. You can folder app deployment into the JSON template (using webdeploy or github) but since that requires access to external artifacts, that's not done automatically.
That help?

Resources