How to use Terraform in a cloud agnostic way How to use Terraform in a cloud agnostic way azure azure

How to use Terraform in a cloud agnostic way


While Terraform as a tool is cloud agnostic (in that it will support anything that exposes its API and has enough developer support to create a "provider" for it), Terraform itself will not natively abstract this at all and I'd seriously consider whether this is a good idea at all unless you have a really good use case.

If you did need to do this you would need to build a bunch of modules on top of things that abstracts the cloud layer from the module users and just allow them to specify the cloud provider as a variable (potentially controllable from some outside script).

As a basic example to abstract DNS you might have something like this (untested):

modules/google/dns/record/main.tf

variable "count" = {}variable "domain_name_record" = {}variable "domain_name_zone" = {}variable "domain_name_target" = {}resource "google_dns_record_set" "frontend" {  count = "${variable.count}"  name  = "${var.domain_name_record}.${var.domain_name_zone}"  type  = "CNAME"  ttl   = 300  managed_zone = "${var.domain_name_zone}"  rrdatas = ["${var.domain_name_target}"]}

modules/aws/dns/record/main.tf

variable "count" = {}variable "domain_name_record" = {}variable "domain_name_zone" = {}variable "domain_name_target" = {}data "aws_route53_zone" "selected" {  count = "${variable.count}"  name  = "${var.domain_name_zone}"}resource "aws_route53_record" "www" {  count   = "${variable.count}"  zone_id = "${data.aws_route53_zone.selected.zone_id}"  name    = "${var.domain_name_record}.${data.aws_route53_zone.selected.name}"  type    = "CNAME"  ttl     = "60"  records = [${var.domain_name_target}]}

modules/generic/dns/record/main.tf

variable "cloud_provider" = { default = "aws" }variable "domain_name_record" = {}variable "domain_name_zone" = {}variable "domain_name_target" = {}module "aws_dns_record" {  source             = "../../aws/dns/record"  count              = "${var.cloud_provider == "aws" ? 1 : 0}"  domain_name_record = "${var.domain_name_record}"  domain_name_zone   = "${var.domain_name_zone}"  domain_name_target = "${var.domain_name_target}"}module "google_dns_record" {  source             = "../../google/dns/record"  count              = "${var.cloud_provider == "google" ? 1 : 0}"  domain_name_record = "${var.domain_name_record}"  domain_name_zone   = "${var.domain_name_zone}"  domain_name_target = "${var.domain_name_target}"}

Obviously this will get complicated pretty fast but it does mean that you can expose the "generic" module to others and allow them to use the abstractions you are building on things. How you cope with things where there isn't feature parity between different clouds is a whole separate question and probably not best suited for StackOverflow.


@ydaetskcoRI love your idea of the provider specific modules (which follow an interface). But the change I would propose is that they can be called from the main.tf file directly with a variable there to determine the module source.

So I would keep:
modules/google/dns/record/main.tf
modules/aws/dns/record/main.tf

I would not use modules/generic/dns/record/main.tf

And add:
main.tf

variable "cloud_provider" = { default = "aws" }  module "dns" {    source = "modules/${var.cloud_provider}/dns/record/main.tf"    domain_name_record = "${var.domain_name_record}"    domain_name_zone   = "${var.domain_name_zone}"    domain_name_target = "${var.domain_name_target}"  }

Unfortunately Terraform doesn't support variable module source.
See: https://github.com/hashicorp/terraform/issues/1439

I'm considering ways to add a plugin or perhaps even a higher level template abstraction.


You cannot do that.

Terraform can be used to create resources in multiple cloud providers in a single terraform apply command.

But you will need separate resources config for different cloud providers.

For example you can create a terraform config file and declare aws_instance and azurerm_virtual_machine resources to create resources in both aws and azure.