Terraform with OneFuse: Better Together

Caution: Articles written for technical not grammatical accuracy, If poor grammar offends you proceed with caution ;-)

If you have read some of my other articles you have heard me mention the OneFuse Terraform provider, but only that it has one.  Well in this article we are going to dive right in and take a first hand look at how awesome it really is.

For those of you that know me, you probably know I’m a long time die hard vRA fanboy going all the way back to before it was vRA in the DynamicOps days.  Well I’m still a bit of a vRA fanboy, but I’m really in love with Terraform.  Not just Terraform, but Terraform with OneFuse.

If you are familiar with Terraform you are probably familiar with all the open source providers that are all built very differently and not kept up to date making it a challenge to reliably consume needed integrations.  Especially for on-prem services.  With OneFuse those days are over.  OneFuse provides a standardized way to consume integrations for Terraform, vRA7, vRA8, as well via it’s super simple Rest API.  Let’s Get started and take a look at some Terraform configurations

If you want to be able to follow along you will need to head over to the OneFuse Guide and follow it until you have your policies created.

OneFuse Naming

Just about everything needs a name, heck in some configurations that I have created I’ve had to name upwards of 30 different things.  This can be a pain especially if they require different standards and different inputs to help determine the appropriate names.  It gets even more painful when you need to ensure they are unique.  Well let’s take a look at the below configuration and unpack it to see what is going on.

// Comment out the following for Terraform 0.12
 
 terraform {
  required_providers {
    onefuse = {
      source  = "CloudBoltSoftware/onefuse"
      version = ">= 1.10.0"
   }
  }
  required_version = ">= 0.13"
}
// Comment out the above for Terraform 0.12
 
// Inititalize OneFuse Provider
provider "onefuse" {
 
  scheme     = var.onefuse_scheme
  address    = var.onefuse_address
  port       = var.onefuse_port
  user       = var.onefuse_user
  password   = var.onefuse_password
  verify_ssl = var.onefuse_verify_ssl
}
 
// OneFuse Data Source for Naming Policy to lookup policy ID
data "onefuse_naming_policy" "policy" {
  name = "default"
}
 
// OneFuse Resource for Naming
resource "onefuse_naming" "machine" {
  naming_policy_id        = data.onefuse_naming_policy.policy.id
  template_properties     = var.onefuse_template_properties
}
 
// Output Results for Naming
output "hostname" {
  value = onefuse_naming.machine.name
}

First let’s look at the Data Source for onefuse_naming_policy.  This is pretty straight forward.  The name value should be the name of your OneFuse Naming Policy that you want to use and it will lookup that policy so we can grab it’s ID to use in the naming resource.

data "onefuse_naming_policy" "policy" {
name = "machine"
}

Next if we look at the Naming Resource it has a few components. The naming_policy_id and the value takes the ID of the policy you wish to use.  In this example we are grabbing it from the data source that we just saw.

The second input template_properties is basically inputs that we need to pass into the policy.  In this case I’m using a variable defined in a variables file that contains the template_properties I want to pass.

resource "onefuse_naming" "machine" {
  naming_policy_id        = data.onefuse_naming_policy.policy.id
  template_properties     = var.onefuse_template_properties
}

The values I am passing for template properties are as follows:

variable "onefuse_template_properties" {
  type = map
  default = {
      "nameEnv"               = "p"
      "nameOS"                = "w"
      "nameDatacenter"        = "por"
      "nameApp"               = "ap"
      "nameLocation"          = "atl"
      "nameGroup"             = "pp"
      "ouGroup"               = "PiedPiper"
      "ouEnv"                 = "PRD"
      "dnsSuffix"             = "infoblox851.sovlabs.net"
      "sgEnv"                 = "prod"
  }
}

Not all of these are used in my naming policy.  To keep things simple I assign values here that I want to use for all the OneFuse modules I will be using in my plan as they all take template_properties.

OneFuse IPAM

OneFuse currently supports Infoblox, Bluecat, and Men & Mice as IPAM providers.  You however don’t need to know which is being used to consume via Terraform.  Because it is extracted and you are consuming a OneFuse IPAM Policy it doesn’t matter.  You simply consume the appropriate policy and OneFuse takes care of the rest.  Below is an example configuration which we will unpack below:

// Comment out the following for Terraform 0.12
 
 terraform {
  required_providers {
    onefuse = {
      source  = "CloudBoltSoftware/onefuse"
      version = ">= 1.10.0"
   }
  }
  required_version = ">= 0.13"
}
// Comment out the above for Terraform 0.12
 
// Inititalize OneFuse Provider
provider "onefuse" {
 
  scheme     = var.onefuse_scheme
  address    = var.onefuse_address
  port       = var.onefuse_port
  user       = var.onefuse_user
  password   = var.onefuse_password
  verify_ssl = var.onefuse_verify_ssl
}
 
// OneFuse Data Source for IPAM Policy to lookup policy ID
data "onefuse_ipam_policy" "policy" {
  name = "default"
}
 
// OneFuse Resource for IPAM Record
resource "onefuse_ipam_record" "ipam-record" {
    
    hostname = var.hostname
    policy_id = data.onefuse_ipam_policy.policy.id
    template_properties = var.onefuse_template_properties
}
 
// Output Results for IPAM Resources
output "ip_address" {
  value = onefuse_ipam_record.ipam-record.ip_address
}
 
output "netmask" {
  value = onefuse_ipam_record.ipam-record.netmask
}
 
output "gateway" {
  value = onefuse_ipam_record.ipam-record.gateway
}
 
output "network" {
  value = onefuse_ipam_record.ipam-record.network
}
 
output "subnet" {
  value = onefuse_ipam_record.ipam-record.network
}
 
output "primary_dns" {
  value = onefuse_ipam_record.ipam-record.network
}
 
output "secondary_dns" {
  value = onefuse_ipam_record.ipam-record.network
}

Like we just saw with Naming there is a OneFuse data source for IPAM.  These data sources exist for all the OneFuse modules so you are able to easily lookup the ID for the policy you would like to use.

data "onefuse_ipam_policy" "policy" {
  name = "default"
}

The IPAM resource has a few additional inputs then what we saw with naming.  This is because IPAM needs additional information that naming does not.  Here you will see we have to pass the hostname to be registered against the IP reservation.  In my example I’m taking it as an input, however as we see later we can grab it directly from the naming module as well.  You will also see the policy_id as well as template_proeprties again.  This will be the case for all modules.

resource "onefuse_ipam_record" "ipam-record" {
    
    hostname = var.hostname
    policy_id = data.onefuse_ipam_policy.policy.id
    template_properties = var.onefuse_template_properties
}

OneFuse DNS

OneFuse currently supports Infoblox, Bluecat, Men & Mice, and Microsoft for DNS registrations.  Just like IPAM you do not need to know which of these the policy is configured to use when using with Terraform.  Below is an example DNS configuration:

// Comment out the following for Terraform 0.12
 
 terraform {
  required_providers {
    onefuse = {
      source  = "CloudBoltSoftware/onefuse"
      version = ">= 1.10.0"
   }
  }
  required_version = ">= 0.13"
}
// Comment out the above for Terraform 0.12
 
// Inititalize OneFuse Provider
provider "onefuse" {
 
  scheme     = var.onefuse_scheme
  address    = var.onefuse_address
  port       = var.onefuse_port
  user       = var.onefuse_user
  password   = var.onefuse_password
  verify_ssl = var.onefuse_verify_ssl
}
 
// OneFuse Data Source for DNS Policy to lookup policy ID
data "onefuse_dns_policy" "policy" {
  name = "prod"
}
 
// OneFuse Resource for DNS Record
resource "onefuse_dns_record" "dns-record" {
    
    name = var.hostname
    policy_id = data.onefuse_dns_policy.policy.id
    zones = [var.dns_zones]
    value = var.ip_address
    template_properties = var.onefuse_template_properties
}

Again we have the DNS data source to look up the policy id.

data "onefuse_dns_policy" "policy" {
  name = "prod"
}

Then we have the DNS resource that will trigger the integration.  The DNS resource has additional inputs that we have not seen before. 

  • name – The name to be associated with the DNS record.
  • zones – Here you specify the zone(s) you want to create a record within.  This supports more than one dns zone.
  • value – Currently IP address is the only value that can be passed as part of value, but in the future support for things like CNAME etc will be supported.

You will also again see template_properties and policy_id as we have seen in previous examples.

resource "onefuse_dns_record" "dns-record" {
    
    name = var.hostname
    policy_id = data.onefuse_dns_policy.policy.id
    zones = [var.dns_zones]
    value = var.ip_address
    template_properties = var.onefuse_template_properties
}

OneFuse Active Directory

By now you should see the pattern forming.  Each module has a data source to lookup the policy ID for the policy you would like to use and then each module has a resource with a set of module specific inputs as well as template_properties that are basically custom inputs.

You can see this repeated below in the sample AD Configuration.

// Comment out the following for Terraform 0.12 to work
 
 terraform {
  required_providers {
    onefuse = {
      source  = "CloudBoltSoftware/onefuse"
      version = ">= 1.10.0"
   }
  }
  required_version = ">= 0.13"
}
// Comment out the above for Terraform 0.12
 
 // Inititalize OneFuse Provider
provider "onefuse" {
 
  scheme     = var.onefuse_scheme
  address    = var.onefuse_address
  port       = var.onefuse_port
  user       = var.onefuse_user
  password   = var.onefuse_password
  verify_ssl = var.onefuse_verify_ssl
}
 
// OneFuse Data Source for AD Policy to lookup policy ID
data "onefuse_ad_policy" "policy" {
  name = "prod"
}
 
// OneFuse Resource for AD Computer Account
resource "onefuse_microsoft_ad_computer_account" "computer" {
    
    name = var.name
    policy_id = data.onefuse_ad_policy.policy.id
    template_properties = var.template_properties
}
 
// Output Result for AD OU Placement
output "ad_ou" {
  value = onefuse_microsoft_ad_computer_account.computer.final_ou
}

On the current release v1.2 OneFuse also has an Ansible Tower Module as well as a Custom Scripting module.  They both function the same as the above modules.  The OneFuse Terraform provider also has support for the OneFuse Property Toolkit (PTK).  I will be covering the Ansible Tower Module, the Scripting Module, and the Property Toolkit in articles dedicated to each module.

For the final example we are going to look at the Naming, IPAM, DNS, & AD modules used together in one Terraform configuration.

Naming, IPAM, DNS, & AD Example

// Comment out the following for Terraform 0.12
 
terraform {
  required_providers {
    onefuse = {
      source  = "CloudBoltSoftware/onefuse"
      version = ">= 1.10.0"
   }
  }
  required_version = ">= 0.13"
}
// Comment out the above for Terraform 0.12
 
// Inititalize OneFuse Provider
provider "onefuse" {
 
  scheme     = var.onefuse_scheme
  address    = var.onefuse_address
  port       = var.onefuse_port
  user       = var.onefuse_user
  password   = var.onefuse_password
  verify_ssl = var.onefuse_verify_ssl
}
 
// OneFuse Data Source for IPAM Policy to lookup policy ID
data "onefuse_ipam_policy" "policy" {
  name = "atlprod"
}
 
// OneFuse Data Source for Naming Policy to lookup policy ID
data "onefuse_naming_policy" "policy" {
  name = "machine"
}
 
// OneFuse Data Source for AD Policy to lookup policy ID
data "onefuse_ad_policy" "policy" {
  name = "prod"
}
 
// OneFuse Data Source for DNS Policy to lookup policy ID
data "onefuse_dns_policy" "policy" {
  name = "prod"
}
 
// OneFuse Data Source for Scripting Policy to lookup policy ID
data "onefuse_scripting_policy" "policy" {
  name = "add_ad_user"
}
 
// OneFuse Resource for Naming Record
resource "onefuse_naming" "name" {
  naming_policy_id        = data.onefuse_naming_policy.policy.id
  dns_suffix              = ""
  template_properties     = var.template_properties
}
 
// OneFuse Resource for AD
resource "onefuse_microsoft_ad_computer_account" "computer" {
    
    name = onefuse_naming.name.name
    policy_id = data.onefuse_ad_policy.policy.id
    template_properties = var.template_properties
}
 
// OneFuse Resource for IPAM Record
resource "onefuse_ipam_record" "ipam-record" {
    
    hostname = onefuse_naming.name.name
    policy_id = data.onefuse_ipam_policy.policy.id
    template_properties = var.template_properties
}
 
// OneFuse Resource for DNS Record
resource "onefuse_dns_record" "dns-record" {
    
    name = onefuse_naming.name.name
    policy_id = data.onefuse_dns_policy.policy.id
    zones = [onefuse_naming.name.dns_suffix]
    value = onefuse_ipam_record.ipam-record.ip_address
    template_properties = var.template_properties
}
 
// Onefuse Scripting Deployment
resource "onefuse_scripting_deployment" "add_ad_user" {
    policy_id = data.onefuse_scripting_policy.policy.id
    template_properties = {
        "username"        = var.username 
        "firstname"       = var.firstname
        "lastname"        = var.lastname
        "domain"          = onefuse_naming.name.dns_suffix
    }
}
 
// Output Results for OneFuse resources
output "hostname" {
  value = onefuse_naming.name.name
}
 
output "ip_address" {
  value = onefuse_ipam_record.ipam-record.ip_address
}
 
output "netmask" {
  value = onefuse_ipam_record.ipam-record.netmask
}
 
output "gateway" {
  value = onefuse_ipam_record.ipam-record.gateway
}
 
output "fqdn" {
  value = format("%s.%s", onefuse_naming.name.name, onefuse_naming.name.dns_suffix)
}
 
output "ad_ou" {
  value = onefuse_microsoft_ad_computer_account.computer.final_ou
}

In the above example you see a simple configuration of how to put multiple modules together in the same Terraform configuration.  In a future article we will look at an example of building a Terraform Module that utilizes OneFuse modules to build easy reusable configurations.  Essentially build reusable Terraform blueprints.  Until next time remember to automate responsibly.

Visit https://onefuse.cloudbolt.io for more information and to join the OneFuse Community Forum

https://github.com/CloudBoltSoftware/onefuse-examples