A head-to-head comparison of Azure Terrafy & Terraformer

You’re under time pressure. You know that you’re only a few clicks away, from being done in the Azure portal. Easy. A few weeks go by, and you’re asked to recreate the resources in with the same configurations. Not so easy anymore… Or is it?

Claes Rytlig
Software Engineer

At cVation, we have long been leveraging “Infrastructure as Code” (IaC) to provision resources to Azure. In fact, that is the only way we spin up Azure resources. However, it is not unheard of that someone has done what can be classified as “Portal Driven Development”, meaning one or more Azure resources have been created using the Azure Portal.

Infrastructure As Code (IaC)

There are multiple tools available when deciding to leverage IaC. One of the most popular tools is “Terraform” developed and maintained by HashiCorp. Since Terraform has matured over the years, many third-party developers have extended the capabilities of what can be done natively using Terraform. In this blog post, we are testing two tools that provide extra capabilities, namely “Azure Terrafy” and “Terraformer”.

The goal of both tools is simple: Convert existing infrastructure to Terraform code.

The setup in Azure

For the tests I have set up two resource groups containing a few resources. Resource group 1 contains the following:

Resource group 2 contains the following:

Both resource groups have only a few resources to make it easy to understand what is imported. However, some of the resources have multiple layers of configuration. For example, the App service (tf-demo2-app) is hosting an Nginx docker image- as illustrated below.

While the content is unimportant, the configuration, hosting an image, is important if we want to take existing infrastructure and redeploy it successfully.

Azure Terrafy 👻

The first tool in line is Azure Terrafy (aztfy). It is pretty straight forward to use. First, run the binary in an empty directory then tell it which Azure resource group to use as source. Afterwards a functional set of Terraform configuration files is available.

The system running aztfy needs to have the Azure CLI installed and requires having been authenticated using ‘az login’.

First, set up the required directories for each of the resource groups- and let us run the tool.

Once the command is run, we are met with the following:

The tool has found 6 items in total, with the resource group included. On the left side, there are light bulbs (💡). These indicate that aztfy successfully can import the resource. Unfortunately, the third item, a virtual machine, cannot be imported automagically by aztfy and needs a helping hand. Luckily, the tool allows for easy specification of resources which cannot be imported.

First, we specify the resource type in Terraform (azurerm_virtual_machine) and then specify the symbolic name that will be used in the configuration, and voilà, the resource can now be imported. Please note that we can also actively skip unwanted resources.

After about 2 or 3 minutes, we are met with:

Easy peasy. Now, a few files have been generated by aztfy, including terraform state files. Of the generated files, the ones containing configuration we might want to change are the provider.tf and main.tf files.

The provider.tf contains the provider block and the Terraform block, specifying which version of ‘azurerm’ we are going to use.

The main.tf contains all the imported resources, including the virtual machine that we had to manually include in the import.

When running terraform plan, we get the following:

Success! Our Portal driven development has now been translated to Terraform files and actively codified.

Let us try with our app service:

Aztfy found 110 items related to the app service. Upon inspection we find that 103 items are snapshots saved in Azure and these can be skipped. Again, it looks like some of the resources need a helping hand with getting imported. The Microsoft.Web/sites is the actual app service (number 2 on the list). One of the cool features of aztfy is that by pressing "r" on a selected resource it will give us recommendations of possible resource types.

Unfortunately, not all types of Terraform resources are supported by recommendations. The resource types presented in the recommendations are only partly correct, more specifically the azurerm_app_service is incorrect as this is a deprecated Terraform resource – The azure_linux_web_app, not presented by the recommendations is needed instead.

When the Terraform files have been created, let us try running a “Terraform plan” to see how we are:

It seems like the azurerm_app_service_plan has been superseded by the azurerm_service_plan. Besides the warning, it appears that everything looks good… On the surface at least. Referring to the initial presentation of the app service plan, we had an Nginx docker image included on our app service, but in the generated configuration, that is nowhere to be found. It would require additional manual configuration to get that included.

Next up: Terraformer

Next tool in line is Terraformer. It is not as straightforward as Terrafy, mainly due to more configurability and command line inputs.

Contrary to aztfy, Terraformer requires a few environment variables to be set (subscription id, client id, client secret and tenant id) due to an ongoing bug where one needs to use a service principal instead of a user account. Since Terraformer is not only limited to Azure, a provider block specifying that we want to use Azure is needed as well.

Once they are set and “Terraform init” has run, we can start importing. Terraformer has quite a few configurations, and it is possible to convert each resource into its own file, but for this demo we will use the compact version which generates all our configurations to a single file.

When the import is done, we get the following files:

When running “Terraform plan” on the newly generated code, we get a bunch of errors related to the managed disk specifying that the arguments given are incorrect.

Contrary to Aztfy, too much is returned by the tool, as some of the configurations are not necessarily needed. The very first error is related to a fault domain and requires a VM scale set ID to be given. This does not make a lot of sense when we only have a single VM with no availability sets enabled. This should simply be deleted from the configuration. Unfortunately, the same pattern continues throughout the error messages, and configurations have been set where they are not needed.

Now we start the same procedure for the app service. When the import is complete and we test whether the configuration is deployable, we get the following:

Not exactly unexpected, we get similar errors to the previous demo. Some of the configurations should have been excluded from the import. As with Aztfy, the same warning is present with regards to the app service. It has imported a now deprecated version of the app service.

After fixing some of the errors, we are now able to successfully run a plan. Although, from the plan we are presented with “0 to add, 1 to change, 0 to destroy”, meaning Terraformer has not successfully imported all three resources (app service plan, app service and resource group). The app service plan is unfortunately missing and needs to be written without the help of the tool.

The verdict and next steps

After testing both tools it is obvious that they can work as an easy way to codify existing infrastructure but neither provide a silver bullet solution. “Aztfy” is from this test the clear winner in terms of “ease of use” as not a lot of pre-configurations were needed. At the same time, the CLI GUI provides a clear overview of what can and cannot be imported. “Terraformer” on the other hand provides too many irrelevant settings in the imported files, causing a lot of errors, thus requiring a few tweaks in the files to create a successful plan. Further, the generated files are rather static which require a lot of refactoring to increase reusability, specifically, defining variables and outputs, and going a step further, also defining the resources into reusable modules. In addition, the state file used by Terraform is also by default stored locally but should be stored remotely in a secure way.

The examples tested were also quite simple and it is not difficult to imagine a resource group containing quite a few more resources than only a VM or a simple app service. Conclusively, it is without a doubt advantageous and easier to have defined the infrastructure to begin with rather than importing it later. If importing is needed, however, it is great knowing that there are tools out there to accelerate this process and both tools do help jump one of the biggest hurdles; defining the resources.

Want more?

APIOps with Azure API Management

There is a growing need to expose services via APIs, both internally in organizations as well as externally to customers and suppliers. This creates the need to ensure a consistent way to develop, expose, document and maintain APIs between development teams.

Read the blog post now