Skip to content

Commit

Permalink
Merge pull request #805 from SudoSpartanDan/oidc
Browse files Browse the repository at this point in the history
Support for OIDC Authentication
  • Loading branch information
manicminer authored Jun 8, 2022
2 parents a37e6ff + 865431b commit dbca12e
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 2 deletions.
1 change: 1 addition & 0 deletions docs/guides/azure_cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Terraform supports a number of different methods for authenticating to Azure:
* [Authenticating to Azure using Managed Identity](managed_service_identity.html)
* [Authenticating to Azure using a Service Principal and a Client Certificate](service_principal_client_certificate.html)
* [Authenticating to Azure using a Service Principal and a Client Secret](service_principal_client_secret.html)
* [Authenticating to Azure using a Service Principal and OpenID Connect](service_principal_oidc.html)

---

Expand Down
1 change: 1 addition & 0 deletions docs/guides/managed_service_identity.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Terraform supports a number of different methods for authenticating to Azure:
* Authenticating to Azure using Managed Identity (covered in this guide)
* [Authenticating to Azure using a Service Principal and a Client Certificate](service_principal_client_certificate.html)
* [Authenticating to Azure using a Service Principal and a Client Secret](service_principal_client_secret.html)
* [Authenticating to Azure using a Service Principal and OpenID Connect](service_principal_oidc.html)

---

Expand Down
3 changes: 2 additions & 1 deletion docs/guides/service_principal_client_certificate.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Terraform supports a number of different methods for authenticating to Azure:
* [Authenticating to Azure using Managed Identity](managed_service_identity.html)
* Authenticating to Azure using a Service Principal and a Client Certificate (covered in this guide)
* [Authenticating to Azure using a Service Principal and a Client Secret](service_principal_client_secret.html)
* [Authenticating to Azure using a Service Principal and OpenID Connect](service_principal_oidc.html)

---

Expand All @@ -22,7 +23,7 @@ Once you have configured a Service Principal as described in this guide, you sho

## Setting up an Application and Service Principal

A Service Principal is a security principal within Azure Active Directory which can be granted permissions to manage objects in Azure Active Directory. To authenticate with a Service Principal, you will need to create an Application object within Azure Active Directory, which you will use as a means of authentication, either using a [Client Secret](service_principal_client_secret.html) or a Client Certificate (which is documented in this guide). This can be done using the Azure Portal.
A Service Principal is a security principal within Azure Active Directory which can be granted permissions to manage objects in Azure Active Directory. To authenticate with a Service Principal, you will need to create an Application object within Azure Active Directory, which you will use as a means of authentication, either using a [Client Secret](service_principal_client_secret.html), [OpenID Connect](service_principal_oidc.html), or Client Certificate (which is documented in this guide). This can be done using the Azure Portal.

This guide will cover how to generate a client certificate, how to create an Application and linked Service Principal, and then how to assign the Client Certificate to the Application so that it can be used for authentication.

Expand Down
3 changes: 2 additions & 1 deletion docs/guides/service_principal_client_secret.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Terraform supports a number of different methods for authenticating to Azure:
* [Authenticating to Azure using Managed Identity](managed_service_identity.html)
* [Authenticating to Azure using a Service Principal and a Client Certificate](service_principal_client_certificate.html)
* Authenticating to Azure using a Service Principal and a Client Secret (covered in this guide)
* [Authenticating to Azure using a Service Principal and OpenID Connect](service_principal_oidc.html)

---

Expand All @@ -22,7 +23,7 @@ Once you have configured a Service Principal as described in this guide, you sho

## Setting up an Application and Service Principal

A Service Principal is a security principal within Azure Active Directory which can be granted permissions to manage objects in Azure Active Directory. To authenticate with a Service Principal, you will need to create an Application object within Azure Active Directory, which you will use as a means of authentication, either using a [Client Certificate](service_principal_client_certificate.html) or a Client Secret (which is documented in this guide). This can be done using the Azure Portal.
A Service Principal is a security principal within Azure Active Directory which can be granted permissions to manage objects in Azure Active Directory. To authenticate with a Service Principal, you will need to create an Application object within Azure Active Directory, which you will use as a means of authentication, either using a [Client Certificate](service_principal_client_certificate.html), [OpenID Connect](service_principal_oidc.html), or a Client Secret (which is documented in this guide). This can be done using the Azure Portal.

This guide will cover how to create an Application and linked Service Principal, and then how to generate a Client Secret for the Application so that it can be used for authentication.

Expand Down
144 changes: 144 additions & 0 deletions docs/guides/service_principal_oidc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
---
page_title: "Authenticating via a Service Principal and OpenID Connect"
subcategory: "Authentication"
---

# Authenticating using a Service Principal and OpenID Connect

* [Authenticating to Azure using the Azure CLI](azure_cli.html)
* [Authenticating to Azure using Managed Identity](managed_service_identity.html)
* [Authenticating to Azure using a Service Principal and a Client Certificate](service_principal_client_certificate.html)
* [Authenticating to Azure using a Service Principal and a Client Secret](service_principal_client_secret.html)
* Authenticating to Azure using a Service Principal and OpenID Connect (covered in this guide)

---

We recommend using either a Service Principal or Managed Identity when running Terraform non-interactively (such as when running Terraform in a CI server) - and authenticating using the Azure CLI when running Terraform locally.

Once you have configured a Service Principal as described in this guide, you should follow the [Configuring a Service Principal for managing Azure Active Directory](service_principal_configuration.html) guide to grant the Service Principal necessary permissions to create and modify Azure Active Directory objects such as users and groups.

---

## Setting up an Application and Service Principal


A Service Principal is a security principal within Azure Active Directory which can be granted permissions to manage objects in Azure Active Directory. To authenticate with a Service Principal, you will need to create an Application object within Azure Active Directory, which you will use as a means of authentication, either using a [Client Secret](service_principal_client_secret.html), [Client Certificate](service_principal_client_certificate.html), or OpenID Connect (which is documented in this guide). This can be done using the Azure Portal.

This guide will cover how to create an Application and linked Service Principal, and then how to assign federated identity credentials to the Application so that it can be used for authentication via OpenID Connect.

## Creating the Application and Service Principal

We're going to create the Application in the Azure Portal - to do this navigate to the [**Azure Active Directory** overview][azure-portal-aad-overview] within the [Azure Portal][azure-portal] - then select the [**App Registrations** blade][azure-portal-applications-blade]. Click the **New registration** button at the top to add a new Application within Azure Active Directory. On this page, set the following values then press **Create**:

- **Name** - this is a friendly identifier and can be anything (e.g. "Terraform")
- **Supported Account Types** - this should be set to "Accounts in this organisational directory only (single tenant)"
- **Redirect URI** - you should choose "Web" in for the URI type. the actual value can be left blank

At this point the newly created Azure Active Directory application should be visible on-screen - if it's not, navigate to the [**App Registration** blade][azure-portal-applications-blade] and select the Azure Active Directory application.

At the top of this page, you'll need to take note of the "Application (client) ID" and the "Directory (tenant) ID", which you can use for the values of `client_id` and `tenant_id` respectively.

## Configure Azure Active Directory Application to Trust a GitHub Repository

An application will need a federated credential specified for each GitHub Environment, Branch Name, Pull Request, or Tag based on your use case. For this example, we'll give permission to `main` branch workflow runs.

-> **Tip:** You can also configure the Application using the [azuread_application](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application) and [azuread_application_federated_identity_credential](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application_federated_identity_credential) resources in the AzureAD Terraform Provider.


### Via the Portal

On the Azure Active Directory application page, go to **Certificates and secrets**.

In the Federated credentials tab, select Add credential. The Add a credential blade opens. In the **Federated credential scenario** drop-down box select **GitHub actions deploying Azure resources**.

Specify the **Organization** and **Repository** for your GitHub Actions workflow. For **Entity type**, select **Environment**, **Branch**, **Pull request**, or **Tag** and specify the value. The values must exactly match the configuration in the GitHub workflow. For our example, let's select **Branch** and specify `main`.

Add a **Name** for the federated credential.

The **Issuer**, **Audiences**, and **Subject identifier** fields autopopulate based on the values you entered.

Click **Add** to configure the federated credential.

### Via the Azure API

```sh
az rest --method POST \
--uri https://graph.microsoft.com/beta/applications/${APP_OBJ_ID}/federatedIdentityCredentials \
--headers Content-Type='application/json' \
--body @body.json
```

Where the body is:

```json
{
"name":"${REPO_NAME}-pull-request",
"issuer":"https://token.actions.githubusercontent.com",
"subject":"repo:${REPO_OWNER}/${REPO_NAME}:refs:refs/heads/main",
"description":"${REPO_OWNER} PR",
"audiences":["api://AzureADTokenExchange"],
}
```

See the [official documentation](https://docs.microsoft.com/en-us/azure/active-directory/develop/workload-identity-federation-create-trust-github) for more details.

## Configuring Terraform to use OIDC

~> **Note:** If using the AzureRM Backend you may also need to configure OIDC there too, see [the documentation for the AzureRM Backend](https://www.terraform.io/language/settings/backends/azurerm) for more information.

As we've obtained the credentials for this Service Principal - it's possible to configure them in a few different ways.

When storing the credentials as Environment Variables, for example:

```bash
$ export ARM_CLIENT_ID="00000000-0000-0000-0000-000000000000"
$ export ARM_SUBSCRIPTION_ID="00000000-0000-0000-0000-000000000000"
$ export ARM_TENANT_ID="00000000-0000-0000-0000-000000000000"
```

The provider will detect the `ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN` environment variables set by GitHub. You can also specify the `ARM_OIDC_REQUEST_TOKEN` and `ARM_OIDC_REQUEST_URL` environment variables.

For GitHub Actions workflows, you'll need to ensure the workflow has `write` permissions for the `id-token`.

```yaml
permissions:
id-token: write
contents: read
```
For more information about OIDC in GitHub Actions, see [official documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-cloud-providers).
The following Terraform and Provider blocks can be specified - where `2.23.0` is the version of the Azure Provider that you'd like to use:

```hcl
# We strongly recommend using the required_providers block to set the
# Azure Provider source and version being used
terraform {
required_providers {
azuread = {
source = "hashicorp/azuread"
version = "=2.23.0"
}
}
}
# Configure the Microsoft Azure Provider
provider "azuread" {
use_oidc = true
features {}
}
```

-> **Note:** Support for OpenID Connect was added in version 2.23.0 of the Terraform AzureAD provider.

~> **Note:** If using the AzureRM Backend you may also need to configure OIDC there too, see [the documentation for the AzureRM Backend](https://www.terraform.io/language/settings/backends/azurerm) for more information.

More information on [the fields supported in the Provider block can be found here](../index.html#argument-reference).

At this point running either `terraform plan` or `terraform apply` should allow Terraform to run using the Service Principal to authenticate.

Next you should follow the [Configuring a Service Principal for managing Azure Active Directory](service_principal_configuration.html) guide to grant the Service Principal necessary permissions to create and modify Azure Active Directory objects such as users and groups.

[azure-portal]: https://portal.azure.com/
[azure-portal-aad-overview]: https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/Overview
[azure-portal-applications-blade]: https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps/RegisteredApps/Overview

12 changes: 12 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,18 @@ More information on [how to configure a Service Principal using a Client Secret

---

When authenticating as a Service Principal using Open ID Connect, the following fields can be set:

* `oidc_request_token` - (Optional) The bearer token for the request to the OIDC provider. This can also be sourced from the `ARM_OIDC_REQUEST_TOKEN` or `ACTIONS_ID_TOKEN_REQUEST_TOKEN` Environment Variables.

* `oidc_request_url` - (Optional) The URL for the OIDC provider from which to request an ID token. This can also be sourced from the `ARM_OIDC_REQUEST_URL` or `ACTIONS_ID_TOKEN_REQUEST_TOKEN` Environment Variables.

* `use_oidc` - (Optional) Should OIDC be used for Authentication? This can also be sourced from the `ARM_USE_OIDC` Environment Variable. Defaults to `false`.

More information on [how to configure a Service Principal using OpenID Connect can be found in this guide](guides/service_principal_oidc.html).

---

When authenticating using Managed Identity, the following fields can be set:

* `msi_endpoint` - (Optional) The path to a custom endpoint for Managed Identity - in most circumstances this should be detected automatically. This can also be sourced from the `ARM_MSI_ENDPOINT` environment variable.
Expand Down
25 changes: 25 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,28 @@ func AzureADProvider() *schema.Provider {
Description: "The application password to use when authenticating as a Service Principal using a Client Secret",
},

// OIDC specific fields
"use_oidc": {
Type: schema.TypeBool,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("ARM_USE_OIDC", false),
Description: "Allow OpenID Connect to be used for authentication",
},

"oidc_request_token": {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.MultiEnvDefaultFunc([]string{"ARM_OIDC_REQUEST_TOKEN", "ACTIONS_ID_TOKEN_REQUEST_TOKEN"}, ""),
Description: "The bearer token for the request to the OIDC provider. For use when authenticating as a Service Principal using OpenID Connect.",
},

"oidc_request_url": {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.MultiEnvDefaultFunc([]string{"ARM_OIDC_REQUEST_URL", "ACTIONS_ID_TOKEN_REQUEST_URL"}, ""),
Description: "The URL for the OIDC provider from which to request an ID token. For use when authenticating as a Service Principal using OpenID Connect.",
},

// CLI authentication specific fields
"use_cli": {
Type: schema.TypeBool,
Expand Down Expand Up @@ -197,8 +219,11 @@ func providerConfigure(p *schema.Provider) schema.ConfigureContextFunc {
ClientCertPassword: d.Get("client_certificate_password").(string),
ClientCertPath: d.Get("client_certificate_path").(string),
ClientSecret: d.Get("client_secret").(string),
IDTokenRequestURL: d.Get("oidc_request_token").(string),
IDTokenRequestToken: d.Get("oidc_request_url").(string),
EnableClientCertAuth: true,
EnableClientSecretAuth: true,
EnableGitHubOIDCAuth: d.Get("use_oidc").(bool),
EnableAzureCliToken: d.Get("use_cli").(bool),
EnableMsiAuth: d.Get("use_msi").(bool),
MsiEndpoint: d.Get("msi_endpoint").(string),
Expand Down
41 changes: 41 additions & 0 deletions internal/provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,47 @@ func TestAccProvider_clientSecretAuth(t *testing.T) {
}
}

func TestAccProvider_oidcAuth(t *testing.T) {
if os.Getenv("TF_ACC") == "" {
return
}

provider := AzureADProvider()
ctx := context.Background()

// Support only oidc authentication
provider.ConfigureContextFunc = func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
envName := d.Get("environment").(string)
env, err := environments.EnvironmentFromString(envName)
if err != nil {
t.Fatalf("configuring environment %q: %v", envName, err)
}

authConfig := &auth.Config{
Environment: env,
TenantID: d.Get("tenant_id").(string),
ClientID: d.Get("client_id").(string),

EnableGitHubOIDCAuth: true,
IDTokenRequestToken: d.Get("oidc_request_token").(string),
IDTokenRequestURL: d.Get("oidc_request_url").(string),
}

return buildClient(ctx, provider, authConfig, "")
}

d := provider.Configure(ctx, terraform.NewResourceConfigRaw(nil))
if d != nil && d.HasError() {
t.Fatalf("err: %+v", d)
}

if errs := testCheckProvider(provider); len(errs) > 0 {
for _, err := range errs {
t.Error(err)
}
}
}

func testCheckProvider(provider *schema.Provider) (errs []error) {
client := provider.Meta().(*clients.Client)

Expand Down

0 comments on commit dbca12e

Please sign in to comment.