This bundle provides an example of how you can use Porter to build Terraform-based bundles. The example provided here will create Azure CosmosDB and Azure EventHubs objects using Terraform configurations and the porter-terraform mixin. This sample also shows how the Terraform mixin can be used with other mixins, in this case the ARM mixin. The ARM mixin is first used to create an Azure storage account that will be used to configure the Terraform azurerm
backend. It is possible to build bundles using just the porter-terraform mixin, but this example shows you how to use outputs between steps as well.
This bundle will create resources in Azure. In order to do this, you'll first need to create an Azure account if you don't already have one.
The bundle will use an Azure Service Principal in order to authenticate with Azure. Once you have an account, create a Service Principal for use with the bundle. You can do this via the Azure portal, or via the Azure CLI:
-
Create a service principal with the Azure CLI:
az ad sp create-for-rbac --name porterraform -o table
-
Save the values from the command output in environment variables:
Bash
export AZURE_TENANT_ID=<Tenant> export AZURE_CLIENT_ID=<AppId> export AZURE_CLIENT_SECRET=<Password>
PowerShell
$env:AZURE_TENANT_ID = "<Tenant>" $env:AZURE_CLIENT_ID = "<AppId>" $env:AZURE_CLIENT_SECRET = "<Password>"
You will also need to have Porter and Docker installed before you proceed.
If you examine the contents of this example, you will see a porter.yaml
file and a terraform
directory. The porter.yaml
is the bundle definition that will be used to build and run the bundle. Please see the porter.yaml for the contents of the file. In this file, you will find a few credential definitions:
credentials:
- name: subscription_id
env: AZURE_SUBSCRIPTION_ID
- name: tenant_id
env: AZURE_TENANT_ID
- name: client_id
env: AZURE_CLIENT_ID
- name: client_secret
env: AZURE_CLIENT_SECRET
These credentials will correspond to the Azure Service Principal you created above. You'll also notice some parameter definitions:
parameters:
- name: location
type: string
default: "EastUS"
- name: resource_group_name
type: string
default: "porter-terraform"
- name: storage_account_name
type: string
default: "porterstorage"
- name: storage_container_name
type: string
default: "portertf"
- name: storage_rg
type: string
default: "porter-storage"
- name: database-name
type: string
default: "porter-terraform"
These represent the values that can be provided at runtime when installing the bundle.
Finally, please note how the porter-terraform
mixin is used:
- terraform:
description: "Create Azure CosmosDB and Event Hubs"
backendConfig:
key: ${ bundle.name }}.tfstate
storage_account_name: ${ bundle.parameters.storage_account_name }
container_name: ${ bundle.parameters.storage_container_name }
access_key: ${ bundle.outputs.storage_account_key }
vars:
subscription_id: ${bundle.credentials.subscription_id}
tenant_id: ${bundle.credentials.tenant_id}
client_id: ${bundle.credentials.client_id}
client_secret: ${bundle.credentials.client_secret}
database_name: ${bundle.parameters.database-name}
resource_group_name: ${bundle.parameters.resource_group_name}
resource_group_location: ${bundle.parameters.location}
outputs:
- name: cosmos-db-uri
- name: eventhubs_connection_string
This mixin step uses both parameters and credentials, defined above, and declares two output values. The source of these output files is defined in the Terraform configuration.
The terraform
directory contains the Terraform configuration files that will be used by the porter-terraform
mixin:
ls -l terraform/
total 40
-rw-r--r-- 1 jeremyrickard staff 1023 Jul 2 08:32 cosmos-db.tf
-rw-r--r-- 1 jeremyrickard staff 622 Jul 2 08:30 eventhubs.tf
-rw-r--r-- 1 jeremyrickard staff 290 Jul 3 10:43 main.tf
-rw-r--r-- 1 jeremyrickard staff 286 Jul 3 17:09 outputs.tf
-rw-r--r-- 1 jeremyrickard staff 325 Jul 2 08:11 variables.tf
The main.tf
file configures the azurerm
provider, while the outputs.tf
defines the outputs we will capture from the Terraform step. cosmos-db.tf
and eventhubs.tf
contain the declarations for the infrastructure we will create. Finally, variables.tf
defines a set of variables used throughout the files. These correspond to the parameters in our porter.yaml
above.
In order to use this bundle, you'll first need to build it. This is done with the porter
command line tool. Ensure that your working directory is set to this example directory before proceeding, then run the following command:
porter build
Once this command has finished, you will see a .cnab
directory created in the working directory. This directory contains the Dockerfile
for the bundle's installer image generated by Porter and the porter-terraform
mixin:
$ more .cnab/Dockerfile
FROM debian:stable-slim
ARG BUNDLE_DIR
RUN apt-get update && apt-get install -y ca-certificates
ENV TERRAFORM_VERSION=1.0.4
RUN apt-get update && apt-get install -y wget unzip && \
wget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip && \
unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip -d /usr/bin && \
rm terraform_${TERRAFORM_VERSION}_linux_amd64.zip
COPY . ${BUNDLE_DIR}
RUN cd /cnab/app/terraform && terraform init -backend=false
# exec mixin has no buildtime dependencies
COPY . ${BUNDLE_DIR}
RUN rm ${BUNDLE_DIR}/porter.yaml
RUN rm -fr ${BUNDLE_DIR}/.cnab
COPY .cnab /cnab
WORKDIR ${BUNDLE_DIR}
CMD ["/cnab/app/run"]
The Dockerfile contains the necessary instructions to install Terraform within our bundle's invocation image.
Before you can install the bundle, you'll need to generate a credential set. This credential set will map your local service principal environment variables into the destinations defined in the porter.yaml
. You can generate the credential set by running porter credentials generate
. This will prompt you for the source for each credential defined in the bundle:
$ porter credentials generate
Generating new credential azure-terraform from bundle azure-terraform
==> 4 credentials required for bundle azure-terraform
? How would you like to set credential "client_id" [Use arrows to move, space to select, type to filter]
specific value
> environment variable
file path
shell command
For each of the credentials, provide the corresponding environment variable that you set above. For example, for client_id
, select environment variable
and provide the value AZURE_CLIENT_ID
.
$ porter credentials generate
Generating new credential azure-terraform from bundle azure-terraform
==> 4 credentials required for bundle azure-terraform
? How would you like to set credential "client_id" environment variable
? Enter the environment variable that will be used to set credential "client_id" AZURE_CLIENT_ID
? How would you like to set credential "client_secret" environment variable
? Enter the environment variable that will be used to set credential "client_secret" AZURE_CLIENT_SECRET
? How would you like to set credential "subscription_id" environment variable
? Enter the environment variable that will be used to set credential "subscription_id" AZURE_SUBSCRIPTION_ID
? How would you like to set credential "tenant_id" environment variable
? Enter the environment variable that will be used to set credential "tenant_id" AZURE_TENANT_ID
Saving credential to /Users/jeremyrickard/.porter/credentials/azure-terraform.yaml
Similar to generating a credential set, you may generate a parameter set if you wish to supply custom values for some/all of the parameters. However, all of the required parameters have default values declared in the Porter manifest, so this step is optional.
To generate a parameter set, run porter parameters generate
. The parameter set can then be supplied on install via -p azure-terraform
.
Once you have built the bundle and generated a credential set, you're ready to install the bundle! To do that, you'll use the porter install
command:
$ porter install -c azure-terraform
installing azure-terraform...
executing install action from azure-terraform (installation: azure-terraform)
Create an Azure Storage Account
Starting deployment operations...
Finished deployment operations...
Create Azure CosmosDB and Event Hubs
Initializing Terraform...
/usr/bin/terraform terraform init -backend=true -backend-config=access_key=******* -backend-config=container_name=portertf -backend-config=key=azure-terraform.tfstate -backend-config=storage_account_name=porterstorage -reconfigure
Initializing the backend...
Successfully configured the backend "azurerm"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
Acquiring state lock. This may take a few moments...
azurerm_resource_group.rg: Creating...
<OUTPUT TRUNCATED>
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
Outputs:
cosmos-db-uri = <sensitive>
eventhubs_connection_string = <sensitive>
eventhubs_topic = "porterform-eventhub"
execution completed successfully!
Installing the bundle will take some amount of time, especially the Azure deployments of the CosmosDB and EventHubs resources.
Assuming all goes well, you should see the relevant resource outputs printed and execution completed successfully!
Now that the bundle has been installed, outputs can be inspected in the following ways.
First, we can get an overview of this installation via porter installation show
:
$ porter installation show
Name: azure-terraform
Created: 4 minutes ago
Modified: 4 minutes ago
Outputs:
-------------------------------------------------------------------------------------------------------------
Name Type Value
-------------------------------------------------------------------------------------------------------------
cosmos-db-uri string mongodb://porterform-cosmos-db:6At4uDIX0o9k1eMot8kRgjldfe...
eventhubs_connection_string string Endpoint=sb://porterform-eventhub-ns.servicebus.windows.n...
io.cnab.outputs.invocationImageLogs string executing install action from
azure-terraform (installati...
storage_account_key string p6UVIHV7kF4n+/AzzUh7Usd8DzRPJJa5H9KRwrLau0mjcG6QRw298dUxY...
History:
------------------------------------------------------------------------------
Run ID Action Timestamp Status Has Logs
------------------------------------------------------------------------------
01FDT6GPBXZJD1DEY5ZW6CB9Y8 install 4 minutes ago succeeded true
We can also show a specific output via porter installation output show [NAME]
:
$ porter installation output show cosmos-db-uri
mongodb://porterform-cosmos-db:6At4uDIX0o9k1eMot8kRgjldfeRBDqONIK7bXD03u3GXi1S1YkX2Rfmxj8hqunbxzlU9KLqtZLe3LM7g4GzPAg==@porterform-cosmos-db.mongo.cosmos.azure.com:10255/?ssl=true&replicaSet=globaldb&retrywrites=false&maxIdleTimeMS=120000&appName=@porterform-cosmos-db@
When you're ready to uninstall the bundle, simply run the porter uninstall
command:
$ porter uninstall -c azure-terraform
uninstalling azure-terraform...
executing uninstall action from azure-terraform (installation: azure-terraform)
Remove Azure CosmosDB and Event Hubs
Initializing Terraform...
/usr/bin/terraform terraform init -backend=true -backend-config=access_key=******* -backend-config=container_name=portertf -backend-config=key=azure-terraform.tfstate -backend-config=storage_account_name=porterstorage -reconfigure
Initializing the backend...
Successfully configured the backend "azurerm"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
Acquiring state lock. This may take a few moments...
azurerm_resource_group.rg: Refreshing state...
<OUTPUT TRUNCATED>
azurerm_resource_group.rg: Destruction complete after 33s
Destroy complete! Resources: 5 destroyed.
Remove the Azure Storage Account
execution completed successfully!
This will take a number of minutes to finish, but when complete the resources will be removed from your account.