# Configuring Workload Identity and Azure Federated Credentials

Teleport Workload Identity issues flexible short-lived identities in JWT format. Azure Federated Credentials allows you to use these JWTs to authenticate to Azure services.

This can be useful in cases where a machine needs to securely authenticate with Azure services without the use of a long-lived credential. This is because the machine can authenticate with Teleport without using any shared secrets by using one of our delegated join methods.

In this guide, we'll configure Teleport Workload Identity and Azure to allow our workload to authenticate to the Azure Blob Storage and upload content to a container.

## How it works

This implementation differs from using the Teleport Application Service to protect Azure APIs in a few ways:

- Requests to Azure are not proxied through the Teleport Proxy Service, meaning reduced latency but also less visibility, as these requests will not be recorded in Teleport's audit log.
- Workload Identity works with any Azure client, including the command-line tool but also their SDKs.
- Using the Teleport Application Service to access Azure does not work with Machine & Workload Identity and therefore cannot be used when a machine needs to authenticate with Azure.

## Prerequisites

- A running Teleport cluster. If you want to get started with Teleport, [sign up](https://goteleport.com/signup) for a free trial or [set up a demo environment](https://goteleport.com/docs/get-started/deploy-community.md).

- The `tctl` and `tsh` clients.

  Installing `tctl` and `tsh` clients

  1. Determine the version of your Teleport cluster. The `tctl` and `tsh` clients must be at most one major version behind your Teleport cluster version. Send a GET request to the Proxy Service at `/v1/webapi/find` and use a JSON query tool to obtain your cluster version. Replace teleport.example.com:443 with the web address of your Teleport Proxy Service:

     ```
     $ TELEPORT_DOMAIN=teleport.example.com:443
     $ TELEPORT_VERSION="$(curl -s https://$TELEPORT_DOMAIN/v1/webapi/find | jq -r '.server_version')"
     ```

  2. Follow the instructions for your platform to install `tctl` and `tsh` clients:

     **Mac**

     Download the signed macOS .pkg installer for Teleport, which includes the `tctl` and `tsh` clients:

     ```
     $ curl -O https://cdn.teleport.dev/teleport-${TELEPORT_VERSION?}.pkg
     ```

     In Finder double-click the `pkg` file to begin installation.

     ---

     DANGER

     Using Homebrew to install Teleport is not supported. The Teleport package in Homebrew is not maintained by Teleport and we can't guarantee its reliability or security.

     ---

     **Windows - Powershell**

     ```
     $ curl.exe -O https://cdn.teleport.dev/teleport-v${TELEPORT_VERSION?}-windows-amd64-bin.zip
     Unzip the archive and move the `tctl` and `tsh` clients to your %PATH%
     NOTE: Do not place the `tctl` and `tsh` clients in the System32 directory, as this can cause issues when using WinSCP.
     Use %SystemRoot% (C:\Windows) or %USERPROFILE% (C:\Users\<username>) instead.
     ```

     **Linux**

     All of the Teleport binaries in Linux installations include the `tctl` and `tsh` clients. For more options (including RPM/DEB packages and downloads for i386/ARM/ARM64) see our [installation page](https://goteleport.com/docs/installation.md).

     ```
     $ curl -O https://cdn.teleport.dev/teleport-v${TELEPORT_VERSION?}-linux-amd64-bin.tar.gz
     $ tar -xzf teleport-v${TELEPORT_VERSION?}-linux-amd64-bin.tar.gz
     $ cd teleport
     $ sudo ./install
     Teleport binaries have been copied to /usr/local/bin
     ```

* To check that you can connect to your Teleport cluster, sign in with `tsh login`, then verify that you can run `tctl` commands using your current credentials. For example, run the following command, assigning teleport.example.com to the domain name of the Teleport Proxy Service in your cluster and email\@example.com to your Teleport username:
  ```
  $ tsh login --proxy=teleport.example.com --user=email@example.com
  $ tctl status
  Cluster  teleport.example.com
  Version  19.0.0-dev
  CA pin   sha256:abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678
  ```
  If you can connect to the cluster and run the `tctl status` command, you can use your current credentials to run subsequent `tctl` commands from your workstation. If you host your own Teleport cluster, you can also run `tctl` commands on the computer that hosts the Teleport Auth Service for full permissions.
* `tbot` must already be installed and configured on the host where the workloads which need to access Teleport Workload Identity will run. For more information, see the [deployment guides](https://goteleport.com/docs/machine-workload-identity/deployment.md).
* An Azure resource group and subscription you wish to grant the workload access to.

### Deciding on a SPIFFE ID structure

Within Teleport Workload Identity, all identities are represented using a SPIFFE ID. This is a URI that uniquely identifies the entity that the identity represents. The scheme is always `spiffe://`, and the host will be the name of your Teleport cluster. The structure of the path of this URI is up to you.

For the purposes of this guide, we will be granting access to Azure to the `spiffe://example.teleport.sh/svc/example-service` SPIFFE ID.

If you have already deployed Teleport Workload Identity, then you will already have a SPIFFE ID structure in place. If you have not, then you will need to decide on a structure for your SPIFFE IDs.

If you are only using Teleport Workload Identity with Azure Federated Credentials, you may structure your SPIFFE IDs so that they explicitly specify the Azure user-managed identity they are allowed to assume. However, it often makes more sense to name the workload or person that will use the SPIFFE ID. See the [best practices guide](https://goteleport.com/docs/machine-workload-identity/workload-identity/best-practices.md) for further advice.

## Step 1/4. Configure Azure

To configure Azure to accept Workload Identity JWT SVIDs as authentication, you'll need to create an identity to represent that workload within Azure, configure that identity to accept JWT SVIDs issued by your Teleport Cluster as a federated credential, and then grant that identity the necessary permissions within Azure using a role assignment.

### Create a user-managed identity

First, you'll create a user-managed identity to represent the workload within Azure.

1. Browse to the "Managed Identities" section of the Azure Portal.
2. Select "Create" to open the "Create User Assigned Managed Identity" form.
3. Select your resource group and subscription.
4. Enter a unique name for your identity that describes the workload it will represent. In our example, we will use "example-service".
5. Select "Review + create".

You now need to record some key information about the created user-managed identity to be used in future steps.

Browse to the "Properties" section on the created user-managed identity and note down the "Client Id" and "Tenant Id", these values will both be UUIDs.

### Create a federated credential

Next, you need to configure a federated credential for the user-managed identity. This will configure Azure to accept JWT SVIDs issued by your Teleport cluster as a form of authentication for this user-managed identity.

1. Browse to your user-managed identity in the Azure Portal and select the "Federated credentials" page from the "Settings" section.
2. Select "Add Credential" to open the "Add Federated Credential" form.
3. Select "Other" for the "Federated credential scenario".
4. Enter the address of your Teleport Proxy service followed by `/workload-identity` for the "Issuer URL", for example `https://example.teleport.sh/workload-identity`.
5. Enter the SPIFFE ID you have decided on for the workload in the "Subject identifier" field. For example, `spiffe://example.teleport.sh/svc/example-service`. This will control which JWT SVIDs issued by Teleport Workload Identity will be accepted for this user-managed identity.
6. Enter a unique, identifiable name for the federated credential. For example, `example-teleport-sh-svc-example-service`.
7. Click "Add".

### Create a storage account and container

For the purposes of this guide, you'll create a storage account and container for your workload to authenticate to. You can skip this step if you already have resources you wish to grant the workload access to.

First, create a storage account:

1. Browse to the "Storage accounts" section of the Azure Portal and select the "Create" button to open the "Create a storage account" form.
2. Select your resource group and subscription.
3. Enter a unique, identifiable name for the storage account. Record this as you will need it later. In our examples we will use `examplestorageaccount`.
4. Select a region and select "Azure Blob Storage" for the "Primary Service" field.
5. Click "Create".

Now, you can create a storage container:

1. Browse to the "Storage accounts" section of the Azure Portal and select the storage account you created.
2. Browse to the "Containers" section beneath the "Data storage" section.
3. Select "Add container" to open the "New container" form.
4. Enter a unique, identifiable name for the container. Record this as you will need it later. In our examples we will use `examplecontainer`.

### Create a role assignment

Finally, you need to grant the user-managed identity the necessary permissions for your workload to perform the actions it needs to within Azure.

When creating a role assignment in Azure, it can be scoped to different levels:

- Subscription (to grant access to any resource within the subscription)
- Resource Group (to grant access to any resource within the resource group)
- Resource (to grant access to a specific resource)

For the purposes of this guide, we will grant the user-managed identity the `Storage Blob Data Owner` role with the scope of the storage account you have created.

1. Browse to the "Storage accounts" section of the Azure Portal and select the storage account you created.
2. Browse to "Access control (IAM)" in the sidebar, then select "Add" and "Add role assignment".
3. Under "Role" select "Storage Blob Data Owner".
4. Under "Members", for "Assign access to" select "Managed identity" and then press "Select members".
5. For "Managed identity", select "User-assigned managed identity" and then search for and select the user-managed identity you created earlier.
6. Click "Review + assign" and save your changes.

## Step 2/4. Configure Teleport RBAC

Now we need to configure Teleport to allow a JWT to be issued containing the SPIFFE ID we have chosen.

First, you'll create a Workload Identity resource to define the identity and its characteristics. Create a new file called `workload-identity.yaml`:

```
kind: workload_identity
version: v1
metadata:
  name: example-workload-identity
  labels:
    example: getting-started
spec:
  spiffe:
    id: /svc/example-service

```

Replace:

- `example-workload-identity` with a descriptive name for the Workload Identity.
- `/svc/example-service` with the path part of the SPIFFE ID you have chosen.

Apply this to your cluster using `tctl`:

```
$ tctl create -f workload-identity.yaml
```

Next, you'll create a role which grants access to this Workload Identity. Create `role.yaml` with the following content:

```
kind: role
version: v6
metadata:
  name: example-workload-identity-issuer
spec:
  allow:
    workload_identity_labels:
      example: ["getting-started"]
    rules:
    - resources:
      - workload_identity
      verbs:
      - list
      - read

```

Replace:

- `example-workload-identity-issuer` with a descriptive name for the role.
- The labels selector if you have modified the labels of the Workload Identity.

Apply this role to your Teleport cluster using `tctl`:

```
$ tctl create -f role.yaml
```

---

TIP

You can also create and edit roles using the Web UI. Go to **Access -> Roles** and click **Create New Role** or pick an existing role to edit.

---

You now need to assign this role to the bot:

```
$ tctl bots update my-bot --add-roles example-workload-identity-issuer
```

## Step 3/4. Issue Workload Identity JWTs

You'll now configure `tbot` to issue and renew the short-lived JWT SVIDs for your workload. It'll write the JWT as a file on disk, where you can then configure Azure clients and SDKs to read it.

Take your already deployed `tbot` service and configure it to issue SPIFFE SVIDs by adding the following to the `tbot` configuration file:

```
services:
  - type: workload-identity-jwt
    destination:
      type: directory
      path: /opt/workload-identity
    selector:
      name: example-workload-identity
    audiences: ["api://AzureADTokenExchange"]

```

Replace:

- /opt/workload-identity with the directory where you want the JWT to be written.
- example-workload-identity with the name of the Workload Identity you have created.

Restart your `tbot` service to apply the new configuration. You should see a file created at `/opt/workload-identity/jwt_svid` containing the JWT.

## Step 4/4. Configure the Azure CLIs and SDKs

You can now use the issued JWT SVID to authenticate to Azure. How this is configured varies between the Azure CLI and Azure SDK.

### Configuring Azure CLI

To use the JWT SVID to authenticate with the Azure CLI, you perform log-in specifying the JWT and the client and tenant ID of the user-managed identity you created. This performs an initial exchange of the JWT SVID for an Azure access token, which is then cached by the CLI until the access token expires.

Run the following, inserting the client and tenant id you recorded in the first step:

```
$ az login \
  --federated-token $(cat /opt/workload-identity/jwt_svid) \
  --service-principal \
  -u <user-managed-identity-client-id> \
  -t <user-managed-identity-tenant-id>
```

You should see a message indicating that this has succeeded.

You can now test this has succeeded by uploading a file to the Azure Blob Storage container:

```
$ echo "testing 1,2,3..." > test.txt
# Upload a test file. Replace the name of the account and container with those
# you selected earlier.
$ az storage blob upload \
  --auth login \
  --account-name examplestorageaccount \
  --container-name examplecontainer \
  --name test.txt \
  --file ./test.txt
# Check that the file has been uploaded. Replace the name of the account and
# container with those you selected earlier.
$ az storage blob list \
  --auth login \
  --output table \
  --account-name examplestorageaccount \
  --container-name examplecontainer

```

### Configuring Azure SDKs

The Azure SDKs support a set of environment variables to configure them to use the federated credential for authentication with the issued JWT SVID:

- `AZURE_CLIENT_ID`: The client ID of the user-managed identity.
- `AZURE_TENANT_ID`: The tenant ID of the user-managed identity.
- `AZURE_FEDERATED_TOKEN_FILE`: The path to the JWT SVID file, for example `/opt/workload-identity/jwt_svid`.

You can also explicitly configure the SDK to use the federated credential, this will vary language-to-language.

## Next steps

- [Azure Workload Identity Federation](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation): The official Azure documentation for Workload Identity Federation.
- [Workload Identity Overview](https://goteleport.com/docs/machine-workload-identity/workload-identity/introduction.md): Overview of Teleport Workload Identity.
- [JWT SVID Overview](https://goteleport.com/docs/machine-workload-identity/workload-identity/jwt-svids.md): Overview of the JWT SVIDs issued by Teleport Workload Identity.
- [Best Practices](https://goteleport.com/docs/machine-workload-identity/workload-identity/best-practices.md): Best practices for using Workload Identity in Production.
- Read the [configuration reference](https://goteleport.com/docs/reference/machine-workload-identity/configuration.md) to explore all the available configuration options.
