Provisioning IBM Cloud Services With Terraform

This blog post will teach you how to provision applications services on IBM Cloud with Terraform.

Terraform is an open-source “infrastructure-as-code” tool. It allows cloud resources to be defined using a declarative configuration file. The Terraform CLI then uses this file to automatically provision and maintain cloud infrastructure needed by your application. This allows the creation of reproducible environments in the cloud across your application life cycle.

IBM Cloud created an official provider plugin for Terraform. This allows IBM Cloud services to be declared in Terraform configuration files. This is a much better approach than using the CLI or IBM Cloud UI to create application services manually.

The following steps needed to set up Terraform with IBM Cloud will be explained.

  • Install Terraform CLI tools and IBM Cloud Provider Plugin.
  • Create API keys for platform access.
  • Terraform configuration for IBM Cloud services.
  • Terraform CLI commands to provision IBM Cloud services.

Ready? Let’s go! 😎😎😎

Install Terraform

Once installed, the terraform command will be available.

$ terraform
Usage: terraform [-version] [-help] <command> [args]
...

Install IBM Cloud Terraform Plugin

  • Download the IBM Cloud Terraform plugin binary from the Github releases page.
  • Unzip the release archive to extract the plugin binary (terraform-provider-ibm_vX.Y.Z).
  • Move the binary into the Terraform plugins directory for the platform.
    • Linux/Unix/OS X: ~/.terraform.d/plugins
    • Windows: %APPDATA%\terraform.d\plugins

IBM Cloud Authentication Credentials

IBM Cloud’s Terraform provider plugin needs authentication credentials to interact with the platform. This is best handled by creating an API key and exporting as an environment variable. API keys can be created from the IBM Cloud CLI or the web site.

using the cli

ibmcloud iam api-key-create terraform-api-key

The apikey property in the JSON output is the API key value.

{
	"name": "terraform-api-key",
	"description": "...",
	"apikey": "xxx-yyy-zzz",
	"createdAt": "...",
	"locked": false,
	"uuid": "..."
}

Store this value securely. API keys cannot be retrieved after creation!

using the web site.

  • From the IAM Users page, select a user account.
  • Under the “API keys” table, click the “Create an IBM Cloud API Key” button.
  • Give the key a name and (optional) description.
  • Make a note of the API key value returned. API keys cannot be retrieved after creation.

exporting as an environment variable

  • Expose the API key as an environment variable to provide credentials to Terraform.
export BM_API_KEY=API_KEY_VALUE

Terraform configuration

We can now start to write configuration files to describe IBM Cloud services we want to provision. Terraform configuration files are human-readable text files, ending with the .tf extension, which contain HashiCorp Configuration Language (HCL) syntax.

IBM Cloud platform services come in two flavours: IAM managed resource instances and older Cloud Foundry-based service instances. This is due to the history of IBM Cloud starting as Bluemix, a Cloud Foundry-based cloud platform. Both platform services types can be provisioned using Terraform.

Most IBM Cloud platform services are available today as “resource instances”.

create new configuration file

  • Create a new infra.tf file which contains the following syntax.
provider "ibm" {}

add resource instances

Resource instances can be added to the configuration file as follows.

resource "ibm_resource_instance" "resource_instance_name" {
  name              = "test"
  service           = "service-id"
  plan              = "service-plan"
  location          = "region-info"
}
  • resource_instance_name - identifier for this service in the configuration, referenced by service keys.
  • name - user-provided service name used by the platform to identify service.
  • service - service identifier on the platform (can be found in the service documentation page).
  • plan - service plan used for billing.
  • location - cloud region used during service provisioning.

Here is an example of provisioning a Cloudant database using the ibm_resource_instance configuration.

resource "ibm_resource_instance" "cloudant" {
  name              = "my-cloudant-db"
  service           = "cloudantnosqldb"
  plan              = "lite"
  location          = "us-south"
}

Other parameters are supported for resource configuration, see the docs for more details…

add resource keys

Applications accessing resource instances need service credentials. Access keys can also be provisioned using Terraform configuration.

resource "ibm_resource_key" "resource_key_name" {
  name                 = "my-key-name"
  role                 = "<IAM_ROLE>"
  resource_instance_id = "${ibm_resource_instance.resource_instance_name.id}"
}
  • name - user-provided key name used by the platform to identify the credentials.
  • role - IBM Cloud IAM roles (as supported by the service, e.g. Writer or Reader).

Here is an example of provisioning a resource key for the Cloudant example from above.

resource "ibm_resource_key" "cloudant_key" {
  name                  = "my-db-key"
  role                  = "Manager"
  resource_instance_id  = "${ibm_resource_instance.cloudant.id}"
}

(optional) add services instances to configuration

Use the following configuration to provision older Cloud Foundry services.

resource "ibm_service_instance" "service_instance_name" {
  name       = "test"
  space_guid = "cf-space-guid"
  service    = "service-id"
  plan       = "service-plan"
}
  • service_instance_name - identifier for this service in the configuration, referenced by service keys.
  • name - user-provided service name used by the platform to identify the service.
  • service - service identifier on the platform (can be found in the service documentation page).
  • plan - service plan used for billing.

(optional) add service instance keys

Applications accessing service instances need service credentials. Service keys can also be provisioned using Terraform configuration.

resource "ibm_service_key" "service_key_name" {
  name                 = "my-key-name"
  service_instance_guid = "${ibm_service_instance.service_instance_name.id}"
}
  • name - user-provided key name used by the platform to identify the credentials.
  • service_instance_guid - Service instance GUID.

add output configuration

Accessing service keys and other service details is handled with output configuration in Terraform files.

output "app_credentials" {
  value = "${ibm_resource_key.resource_key_name.credentials}"
}

Output values can be logged to the console using the Terraform CLI.

Here is an example of accessing Cloudant credentials provisioned in the example above.

output "cloudant_credentials" {
  value = "${ibm_resource_key.cloudant_key.credentials}"
}

Run Terraform commands

Having finished the configuration file to describe our applications services, the Terraform CLI can now provision those services!

terraform init
  • Validate the configuration file for syntax errors.
terraform validate
  • Display the platform changes to be executed on the configuration file.
terraform plan

Here is the example output from running that command with the Cloudant database example.

Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + ibm_resource_instance.cloudant
      id:                   <computed>
      location:             "us-south"
      name:                 "my-cloudant-db"
      plan:                 "lite"
      service:              "cloudantnosqldb"
      status:               <computed>

  + ibm_resource_key.cloudant_key
      id:                   <computed>
      credentials.%:        <computed>
      name:                 "my-db-key"
      parameters.%:         <computed>
      resource_instance_id: "${ibm_resource_instance.cloudant.id}"
      role:                 "Manager"
      status:               <computed>

Plan: 2 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

  • Execute the planned changes using apply.
terraform apply -auto-approve

Terraform will now provision the platform services, resources keys and output credentials to the console.

Here is the example output from running that command with the Cloudant database example.

ibm_resource_instance.cloudant: Creating...
  location: "" => "us-south"
  name:     "" => "my-cloudant-db"
  plan:     "" => "lite"
  service:  "" => "cloudantnosqldb"
  status:   "" => "<computed>"
ibm_resource_instance.cloudant: Still creating... (10s elapsed)
ibm_resource_instance.cloudant: Still creating... (20s elapsed)
ibm_resource_instance.cloudant: Creation complete after 21s (ID: ...)
ibm_resource_key.cloudant_key: Creating...
  credentials.%:        "" => "<computed>"
  name:                 "" => "my-db-key"
  parameters.%:         "" => "<computed>"
  resource_instance_id: "" => "crn:v1:bluemix:public:cloudantnosqldb:us-south:a/...::"
  role:                 "" => "Manager"
  status:               "" => "<computed>"
ibm_resource_key.cloudant_key: Creation complete after 8s (ID: ...)

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

cloudant_credentials = {
  apikey = <API_KEY_VALUE>
  host = <DB_HOST>
  ...
}

API keys from the cloudant_credentials output section can be used applications to interact with the provisioned database! 👏👏👏

Conclusion

Provisioning cloud services using Terraform is a great way to manage application resources on IBM Cloud.

Applications resources are defined in a declarative configuration file, following the “infrastructure-as-code” approach to managing cloud environments. This configuration is maintained in the application’s source code repository to enable reproducible environments.

IBM Cloud provides an official provider plugin for Terraform. This allows IBM Cloud services to be defined through custom configuration primitives. Developers can then use the Terraform CLI to provision new resources and extract service keys needed to access those services. 💯💯💯