Use SSH keys with Terraform on Azure

February 24, 2016

Recently I’ve been using an awesome tool called Terraform for provisioning temporary cloud infrastructure for performance and scalability testing some software.

I initially started with AWS and then moved on to repeating the same basic infrastructure on Azure. Azure is a bit different from AWS in terms of its resource types but generally maps quite well. One thing that caused an hour of head scratching and frustration was trying to configure the virtual machines I was creating to use SSH keys.

When you create a Linux virtual machine on Azure via the Portal you are given the opportunity to provide a public SSH key to use for authentication. I expected Terraform to provide an ssh_key parameter but instead found an ssh_key_thumbprint parameter. After some digging it turns out you can associate certificates with a “hosted service” in Azure and then reference those when creating virtual machines.

Terraform doesn’t provide support for uploading SSH keys so I came up with a simple solution. Note this requires that you have the Azure CLI tool installed (and that you’ve logged in) as well as OpenSSH.

In your Terraform template take two arguments:

  1. azure_ssh_key_path - The path to the SSH key you want to use for your virtual machines. Note it must be in der format.
  2. azure_ssh_key_fingerprint - The fingerprint of the SSH key.

Then, when creating hosted services, use a local provisioner to upload the key:

variable "azure_ssh_key_path" {}
variable "azure_ssh_key_fingerprint" {}

resource "azure_hosted_service" "hosted_service" {
  name = "my-hosted-service"
  location = "West US"
  ephemeral_contents = true
  provisioner "local-exec" {
    command = "azure service cert create ${self.name} ${var.azure_ssh_key_path}"
  }
}

And finally, when creating a virtual machine inside the hosted service, use the thumbprint:

resource "azure_instance" "client" {
  name = "my-virtual-machine"
  image = "${var.azure_image_name}"
  size = "Standard_G5"
  hosted_service_name = "${azure_hosted_service.hosted_service.name}"
  storage_service_name = "..."
  location = "West US"
  username = "my-user"
  ssh_key_thumbprint = "${var.azure_ssh_key_fingerprint}"
  endpoint {
    name = "SSH"
    protocol = "tcp"
    public_port = 22
    private_port = 22
  }
}

If you need help getting your key in to the right format for Azure you can use this bash script:

SSH_KEY_PATH="~/.ssh/id_rsa"
AZURE_SSH_KEY_PATH="/tmp/azure-deployer.pfx"
openssl req -x509 \
  -key $SSH_KEY_PATH \
  -nodes \
  -days 365 -newkey rsa:2048 \
  -out /tmp/azure-deployer.pem \
  -subj '/CN=www.mydomain.com/O=MyCompany./C=US'
openssl x509 \
  -outform der \
  -in /tmp/azure-deployer.pem \
  -out $AZURE_SSH_KEY_PATH
fingerprint=$(openssl x509 -fingerprint -inform der -in $AZURE_SSH_KEY_PATH | grep "SHA1 Fingerprint")
fingerprint="${fingerprint#*=}"
fingerprint="${fingerprint//:/}"
AZURE_SSH_KEY_FINGERPRINT=$fingerprint

Then pass the AZURE_SSH_KEY_PATH and AZURE_SSH_KEY_FINGERPRINT variables to Terraform:

terraform apply -var azure_ssh_key_path=$AZURE_SSH_KEY_PATH -var azure_ssh_key_fingerprint=$AZURE_SSH_KEY_FINGERPRINT
comments powered by Disqus