TrademarkTrademark
Features
Documentation
All articles

AzureRM Terraform Provider Overview

AzureRM Terraform provider guide: core resources, auth setup, best practices for deploying to Azure with Terraform.
Brendan ThompsonMay 26, 2025
AzureRM Terraform Provider Overview

This post is part of a series on Terraform Providers: Complete Configuration and Management Guide.

At the core of Terraform and OpenTofu is the provider. The provider allows engineers to interact with any number of services on the internet, allowing them to create, update, and destroy resources on that service. The diagram below helps to articulate how exactly this flow works; firstly the Service must provide an API, which is then encapsulated in an SDK, a Terraform provider is then created using the Terraform Plugin Framework, which is then able to be consumed by HCL which acts as a configuration file for our infrastructure as code creating both new resources and data sources. In order to utilize a provider within your Infrastructure as Code, it must first be published to the Terraform registry or a registry that implements the registry interface.

__wf_reserved_inherit

Terraform / OpenTofu Provider Flow

In the rest of this article, we are going to talk about the Terraform AzureRM Provider, this allows the creation of resources with the Microsoft Azure platform using Terraform configuration files. Microsoft actually has two separate types of providers available for engineers to use, the first being the aforementioned AzureRM provider and the second being AzAPI. The latter of the two providers provides direct access to the Azure API and can be a good choice if Azure resources are not yet available in the AzureRM provider. However, I would strongly suggest utilizing the AzureRM provider in your day-to-day configuration as it provides a clear and easy-to-consume interface. The AzureRM provider also gets very regular releases with a weekly cadence and fantastic technical support from HashiCorp, Microsoft, and the community. This regular cadence ensures that security updates and bug fixes are released as quickly as possible meaning less snafus for us engineers, and ensures that we can take advantage of the latest features that both Azure and the provider framework have to offer!

The following section(s) will go through the provider configuration as well as examples of deploying common Azure resources.

Configuration

Firstly, let's look at the most basic provider configuration object we can use for AzureRM:

Unfortunately, even with the recent release of Version 4 of the Azure provider, the features block is still required. This block can be utilized to customize the behaviour of specific Azure Provider resources. (However I have never seen this used). The second requirement is the subscription_id argument as this scopes the provider to a specific Azure subscription, in lieu of actually using the argument an environment variable can be used, that variable is ARM_SUBSCRIPTION_ID, I would generally opt for the use of environment variables as it allows for easier customization at runtime. The big missing item here is authentication, we obviously can't augment resources on the platform without first authenticating to it. Azure offers us a few options for authentication:

As can be seen there are a variety of methods for engineers to authenticate their instance of the AzureRM provider to Azure. In the following examples you could use any of the above, however I will demonstrate with the Service Principal with Client Secret, showing both with environment variables and arguments.

Provider Arguments

The below shows all the required arguments in order to have a properly authenticated AzureRM provider instance. Whilst for the sake of this example the client_secret has a value this should NEVER be done. Either utilize the environment variable or an input variable to pass that argument in at a bare minimum.

Environment Variables

Firstly, we must configure the environment variables, the example below shows this for both bash and PowerShell as they are both valid options.

And now we look at the extremely complex provider configuration:

It is easy to see the advantage of utilizing environment variables as it doesn't require any argument configuration within the provider and it allows it to be managed by an external system such as an orchestrator or CI/CD pipelining tool.

Provider Versions

The next thing to talk about is the all-important ability to constrain versions when it comes to Terraform providers. This is done via the terraform block within your Terraform configuration. The following example will utilize the pessimistic versioning constraint.

It is vitally important to ensure that you have these blocks within required_providers as it makes certain you're getting an expected version from an expected source. Or perhaps you have brought in a copy of providers into your organization for security control that then enforces that the provider is sourced from your internal instance rather than the public one. The following are the valid operators that can be used with version constraints:

  • =: Only the explicitly defined version can be used. (This behaviour is the same when no operator is provided)
  • !=: Excludes the exact defined version.
  • >, >=, <, <=: Version comparators against the defined version.
  • ~>: Ensures only the rightmost element of the defined version may increment (e.g. 1.0 allows for any 1.x but will not allow 2.x)

Nine times out of ten I will choose the last constraint known as the pessimistic version constraint.

Example

Now that we understand how to configure the provider let's look at a single example that creates some foundational Azure resources using the provider. This example will create the following resources:

  • Resource Group — the container/group for resources within Azure a resource cannot exist outside of a Resource Group
  • Virtual Network — the networking construct used with Azure
  • Virtual Machine — an IaaS instance, this can be Windows or Linux and there are resource blocks for both of these individually.

First off we create ourselves a little helper in a locals block this will ensure naming is consistent for all resources we provision:

Normally this would be done with input variables to ensure that it is configurable however in this instance given it's an example I've opted for static values.

The next is our Resource Group creation, this is where all our resources will land. It also helps ensure a consistent location is used for all resources.

As you can see we are referencing our local.suffix, which will yield us the name rg-dev-aue-app.

The next thing for us to do is the network setup, this includes; Virtual Network, Subnet, and Network Interface. Other resources to consider here would be Network Security Groups and Application Security Groups.

The Virtual Network is the foundation of Azure networking, this will generally have a number of Subnets associated with it. Some of which are extremely specific such as the AzureFirewallSubnet which must be named exactly that for it to be consumable by the Azure Firewall.

Next let's look at the final resource within our example, the Virtual Machine, for this example I have selected to use a Linux Virtual Machine as it's the simplest to configure and nobody likes Windows anyway 😜.

The Virtual Machine takes on references for all resources that we have previously created. It will provision a Linux (Ubuntu specifically) instance within our given Resource Group and Virtual Network.

Wrapping Up

In this post we have discussed what a Provider is, the specific implementation of the AzureRM provider including how to configure it and what authentication options are available to us engineers. In addition to this we looked at an example of some common resources being provisioned by the AzureRM provider.

About the author
Brendan Thompsonsolutions engineer at Scalr
Brendan Thompson is a solutions engineer at Scalr, specializing in Terraform and cloud infrastructure.