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:
azure_ssh_key_path
- The path to the SSH key you want to use for your virtual machines. Note it must be inder
format.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