1
votes

Updating a service principles password with Terraform based on when it's going to expire

Setting the service principle up with a password the first time works perfectly, however, I want to expire the password and if the password is going to expire a new one gets generated and updates the service principle with it, I'm not entirely sure how to do conditionals in Terraform as I am still fairly new to Terraform, the docs don't really talk about updating the service principle only creating it and there is no data object to fetch when this is going to expire

So far I have this (full disclosure this is part of a bigger terraform base that I am helping with):

resource "azuread_application" "current" {
  name = "test"
}

resource "azuread_service_principal" "current" {
  application_id = "${azuread_application.current.application_id}"
}

resource "random_string" "password" {
  length  = 64
  special = true
}

resource "azuread_service_principal_password" "current" {
  service_principal_id = "${azuread_service_principal.current.id}"
  value                = "${random_string.password.result}"
  end_date_relative    = "2160h"   # valid for 90 days
}

As the password is only valid for 90 Days I want to run terraform apply just before it expires and update the password

Update 1:

It seems that if indeed you change the azuread_service_principal_password resource, it counts as a change in the dependency tree and recreates the resource you have attached the service principle to, which means there is no way to keep the state in of the service principles credentials in Terraform if they need to be updates

Update 2:

I have attempted to do the following, however the downside to this is that it runs everytime you run terraform apply:

terraform script:

resource "azuread_application" "current" {
  name = "${var.metadata_name}"
}

resource "azuread_service_principal" "current" {
  application_id = "${azuread_application.current.application_id}"
}
resource "random_string" "password" {
  length  = 64
  special = true
}

resource "azuread_service_principal_password" "current" {
  service_principal_id = "${azuread_service_principal.current.id}"
  value                = "${random_string.password.result}"
  end_date_relative    = "2160h"                                   # valid for 90 days
}


resource "null_resource" "password_updater" {
  # Updates everytime you run terraform apply so it will run this script everytime
  triggers {
    timestamp = "${timestamp()}"
  }

  provisioner "local-exec" {
    command = "sh ${path.module}/update_service_password.sh ${azuread_service_principal.current.id} ${var.resource_group} ${azurerm_kubernetes_cluster.current.name}"
  }
}

script:

#!/bin/sh
service_principle_id=$1
resource_group=$2
cluster_name=$3

# get service password expiration
expiration=$(az ad sp list --filter="objectId eq '$service_principle_id'" | jq '.[].passwordCredentials' | jq '.[].endDate' | cut -d'T' -f 1 | cut -d'"' -f 2)

# Format date for condition
now=$(date  +%Y%m%d%H%M%S)
expiration_date=$(date -d "$expiration - 30 days"  +%Y%m%d%H%M%S)


# Compare today with expiration date
if [ ${now} -ge ${expiration_date} ];
then
    # IF expiration date in the next 30 days rest password
    sp_id=$(az aks show -g ${resource_group} -n ${cluster_name} --query servicePrincipalProfile.clientId -o tsv)
    service_principle_secret=$(az ad sp credential reset --name ${sp_id} --end-date $(date -d "+ 90 days"  +%Y-%m-%d) --query password -o tsv)

    # Update cluster with new password
    az aks update-credentials \
    --resource-group ${resource_group} \
    --name ${cluster_name} \
    --reset-service-principal \
    --service-principal ${sp_id} \
    --client-secret ${service_principle_secret}
fi
3
All the ideas i have involve some sort of manual interaction, especially since you state "there is no data object to fetch when this is going to expire". I think you might have to write some sort of local script to determine when the password expired to trigger the Terraform logic. Alternatively, you could PR against the Azure provider or make a feature request.Matt Schuchard

3 Answers

0
votes

For the service principal, the password of it can be reset through the Azure CLI az ad sp reset, but you need to have the permission to do that.

0
votes

I am just going to set this as the Answer as after talking to the developers of the service principle terraform module they have told me it is not possible any other way if, a better way is found please comment:

Answer:

Use the null_resource provider to run a script that runs the update -

resource "azuread_application" "current" {
  name = "${var.metadata_name}"
}

resource "azuread_service_principal" "current" {
  application_id = "${azuread_application.current.application_id}"
}
resource "random_string" "password" {
  length  = 64
  special = true
}

resource "azuread_service_principal_password" "current" {
  service_principal_id = "${azuread_service_principal.current.id}"
  value                = "${random_string.password.result}"
  end_date_relative    = "2160h"                                   # valid for 90 days
}


resource "null_resource" "password_updater" {
  # Updates everytime you run terraform apply so it will run this script everytime
  triggers {
    timestamp = "${timestamp()}"
  }

  provisioner "local-exec" {
    command = "sh ${path.module}/update_service_password.sh ${azuread_service_principal.current.id} ${var.resource_group} ${azurerm_kubernetes_cluster.current.name}"
  }
}

script:

#!/bin/sh
service_principle_id=$1
resource_group=$2
cluster_name=$3

# get service password expiration
expiration=$(az ad sp list --filter="objectId eq '$service_principle_id'" | jq '.[].passwordCredentials' | jq '.[].endDate' | cut -d'T' -f 1 | cut -d'"' -f 2)

# Format date for condition
now=$(date  +%Y%m%d%H%M%S)
expiration_date=$(date -d "$expiration - 30 days"  +%Y%m%d%H%M%S)


# Compare today with expiration date
if [ ${now} -ge ${expiration_date} ];
then
    # IF expiration date in the next 30 days rest password
    sp_id=$(az aks show -g ${resource_group} -n ${cluster_name} --query servicePrincipalProfile.clientId -o tsv)
    service_principle_secret=$(az ad sp credential reset --name ${sp_id} --end-date $(date -d "+ 90 days"  +%Y-%m-%d) --query password -o tsv)

    # Update cluster with new password
    az aks update-credentials \
    --resource-group ${resource_group} \
    --name ${cluster_name} \
    --reset-service-principal \
    --service-principal ${sp_id} \
    --client-secret ${service_principle_secret}
fi
-1
votes

I think a better approach is this:

  1. Your terraform code is most likely wrapped within a bigger process. Most likely you use bash to kick off the process and then terraform. If not - I suggest you do it as this is the best practice with terraform.
  2. In your bash code before running terraform check the expiry of the relevant service principals using az cli, for example. (does not matter)
  3. If expired, use the terraform taint command to mark the service principal password resources as tainted. I do not have the details, maybe you need to taint the service principal too. Maybe not.
  4. If tainted, the terraform would recreate the resources and would regenerate the password.