Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stop requiring subscription_id and update configuration guides #271

Merged
merged 3 commits into from
Jun 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions azuread/data_client_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ func dataClientConfig() *schema.Resource {
},

"subscription_id": {
manicminer marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeString,
Computed: true,
Type: schema.TypeString,
Computed: true,
Deprecated: fmt.Sprintf("The %q attribute will be removed in version 1.0 of the provider. If you are using this attribute, you should instead use the %q data source from the AzureRM provider", "subscription_id", "azurerm_client_config"),
},

"object_id": {
Expand Down Expand Up @@ -62,8 +63,12 @@ func dataSourceArmClientConfigRead(d *schema.ResourceData, meta interface{}) err
d.SetId(time.Now().UTC().String())
d.Set("client_id", client.clientID)
d.Set("object_id", client.objectID)
d.Set("subscription_id", client.subscriptionID)
d.Set("tenant_id", client.tenantID)

// TODO: remove in v1.0
if client.subscriptionID != client.tenantID {
d.Set("subscription_id", client.subscriptionID)
}

return nil
}
21 changes: 21 additions & 0 deletions azuread/data_client_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,27 @@ func TestAccClientConfigDataSource_basic(t *testing.T) {
dsn := "data.azuread_client_config.current"
clientId := os.Getenv("ARM_CLIENT_ID")
tenantId := os.Getenv("ARM_TENANT_ID")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccCheckArmClientConfig_basic,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(dsn, "client_id", clientId),
resource.TestCheckResourceAttr(dsn, "tenant_id", tenantId),
testAzureRMClientConfigGUIDAttr(dsn, "object_id"),
),
},
},
})
}

func TestAccClientConfigDataSource_basicDeprecated(t *testing.T) { // TODO: remove in v1.0
dsn := "data.azuread_client_config.current"
clientId := os.Getenv("ARM_CLIENT_ID")
tenantId := os.Getenv("ARM_TENANT_ID")
subscriptionId := os.Getenv("ARM_SUBSCRIPTION_ID")

resource.ParallelTest(t, resource.TestCase{
Expand Down
15 changes: 13 additions & 2 deletions azuread/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
func Provider() terraform.ResourceProvider {
p := &schema.Provider{
Schema: map[string]*schema.Schema{
// TODO: remove subscription_id field at next major version
"subscription_id": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -100,11 +101,21 @@ func Provider() terraform.ResourceProvider {

func providerConfigure(p *schema.Provider) schema.ConfigureFunc {
return func(d *schema.ResourceData) (interface{}, error) {
// TODO: drop subscription_id in v1.0
// When constructing the Builder, we default to using the tenant ID for the subscription ID.
// Although this has no effect since we never consume it, this practise mimics
// the Azure CLI and it seems the most sensible value to use after a nonsense string.
// However, if subscription_id _is_ configured for the provider, we'll use that since it's
// currently exposed via data.azuread_client_config.
subscriptionId := d.Get("subscription_id").(string)
if subscriptionId == "" {
subscriptionId = d.Get("tenant_id").(string)
}

builder := &authentication.Builder{
// TODO: remove the requirement on the Subscription ID
SubscriptionID: d.Get("subscription_id").(string),
ClientID: d.Get("client_id").(string),
ClientSecret: d.Get("client_secret").(string),
SubscriptionID: subscriptionId,
TenantID: d.Get("tenant_id").(string),
Environment: d.Get("environment").(string),
MsiEndpoint: d.Get("msi_endpoint").(string),
Expand Down
1 change: 0 additions & 1 deletion azuread/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ func TestProvider_impl(t *testing.T) {

func testAccPreCheck(t *testing.T) {
variables := []string{
"ARM_SUBSCRIPTION_ID",
"ARM_CLIENT_ID",
"ARM_CLIENT_SECRET",
"ARM_TENANT_ID",
Expand Down
8 changes: 4 additions & 4 deletions azuread/resource_application_certificate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func TestAccAzureADApplicationCertificate_basic(t *testing.T) {
resourceName := "azuread_application_certificate.test"
ri := tf.AccRandTimeInt()
keyType := "AsymmetricX509Cert"
endDate := time.Now().AddDate(0, 0, 360).UTC().Format(time.RFC3339)
endDate := time.Now().AddDate(0, 6, 0).UTC().Format(time.RFC3339)
value := testCertificateApplication

resource.ParallelTest(t, resource.TestCase{
Expand Down Expand Up @@ -133,7 +133,7 @@ func TestAccAzureADApplicationCertificate_complete(t *testing.T) {
keyId := uuid.New().String()
keyType := "AsymmetricX509Cert"
startDate := time.Now().AddDate(0, 0, 7).UTC().Format(time.RFC3339)
endDate := time.Now().AddDate(0, 0, 360).UTC().Format(time.RFC3339)
endDate := time.Now().AddDate(0, 6, 0).UTC().Format(time.RFC3339)
value := testCertificateApplication

resource.ParallelTest(t, resource.TestCase{
Expand Down Expand Up @@ -195,7 +195,7 @@ func TestAccAzureADApplicationCertificate_requiresImport(t *testing.T) {
resourceName := "azuread_application_certificate.test"
ri := tf.AccRandTimeInt()
keyType := "AsymmetricX509Cert"
endDate := time.Now().AddDate(0, 0, 360).UTC().Format(time.RFC3339)
endDate := time.Now().AddDate(0, 6, 0).UTC().Format(time.RFC3339)
value := testCertificateApplication

resource.ParallelTest(t, resource.TestCase{
Expand Down Expand Up @@ -269,7 +269,7 @@ func testAccADApplicationCertificate_relativeEndDate(ri int, keyType, value stri

resource "azuread_application_certificate" "test" {
application_object_id = "${azuread_application.test.id}"
end_date_relative = "8640h"
end_date_relative = "4320h"
type = "%s"
value = <<EOT
%s
Expand Down
8 changes: 4 additions & 4 deletions azuread/resource_service_principal_certificate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func TestAccAzureADServicePrincipalCertificate_basic(t *testing.T) {
resourceName := "azuread_service_principal_certificate.test"
ri := tf.AccRandTimeInt()
keyType := "AsymmetricX509Cert"
endDate := time.Now().AddDate(0, 0, 360).UTC().Format(time.RFC3339)
endDate := time.Now().AddDate(0, 6, 0).UTC().Format(time.RFC3339)
value := testCertificateServicePrincipal

resource.ParallelTest(t, resource.TestCase{
Expand Down Expand Up @@ -133,7 +133,7 @@ func TestAccAzureADServicePrincipalCertificate_complete(t *testing.T) {
keyId := uuid.New().String()
keyType := "AsymmetricX509Cert"
startDate := time.Now().AddDate(0, 0, 7).UTC().Format(time.RFC3339)
endDate := time.Now().AddDate(0, 0, 360).UTC().Format(time.RFC3339)
endDate := time.Now().AddDate(0, 6, 0).UTC().Format(time.RFC3339)
value := testCertificateServicePrincipal

resource.ParallelTest(t, resource.TestCase{
Expand Down Expand Up @@ -195,7 +195,7 @@ func TestAccAzureADServicePrincipalCertificate_requiresImport(t *testing.T) {
resourceName := "azuread_service_principal_certificate.test"
ri := tf.AccRandTimeInt()
keyType := "AsymmetricX509Cert"
endDate := time.Now().AddDate(0, 0, 360).UTC().Format(time.RFC3339)
endDate := time.Now().AddDate(0, 6, 0).UTC().Format(time.RFC3339)
value := testCertificateServicePrincipal

resource.ParallelTest(t, resource.TestCase{
Expand Down Expand Up @@ -273,7 +273,7 @@ func testAccADServicePrincipalCertificate_relativeEndDate(ri int, keyType, value

resource "azuread_service_principal_certificate" "test" {
service_principal_id = "${azuread_service_principal.test.id}"
end_date_relative = "8640h"
end_date_relative = "4320h"
type = "%s"
value = <<EOT
%s
Expand Down
51 changes: 15 additions & 36 deletions website/docs/guides/azure_cli.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ We recommend using either a Service Principal or Managed Service Identity when r

## Important Notes about Authenticating using the Azure CLI

* Prior to version 1.20 the AzureAD Provider used a different method of authorizing via the Azure CLI where credentials reset after an hour - as such we'd recommend upgrading to version 1.20 or later of the AzureAD Provider.
* Terraform only supports authenticating using the `az` CLI (and this must be available on your PATH) - authenticating using the older `azure` CLI or PowerShell Cmdlets is not supported.
* Terraform only supports authenticating using the `az` CLI (and this must be available on your PATH) - authenticating using the older `azure` CLI or PowerShell Az / AzureRM Cmdlets is not supported.
* Authenticating via the Azure CLI is only supported when using a User Account. If you're using a Service Principal (for example via `az login --service-principal`) you should instead authenticate via the Service Principal directly (either using a [Client Secret](service_principal_client_secret.html) or a [Client Certificate](service_principal_client_certificate.html)).

---
Expand All @@ -44,13 +43,13 @@ Firstly, login to the Azure CLI using:
$ az login
```

Once logged in - it's possible to list the Subscriptions associated with the account via:
Once logged in - it's possible to list the Subscriptions and Tenants associated with the account via:

```shell
$ az account list
```

The output (similar to below) will display one or more Subscriptions - with the `id` field being the `subscription_id` field referenced above.
The output (similar to below) will display one or more Subscriptions - with the `tenantId` field being the `tenant_id` field referenced above.

```json
[
Expand All @@ -69,59 +68,39 @@ The output (similar to below) will display one or more Subscriptions - with the
]
```

Should you have more than one Subscription, you can specify the Subscription to use via the following command:
Should your account exist in more than one tenant (e.g. as a guest user), you can specify the tenant at login time:

```bash
$ az account set --subscription="SUBSCRIPTION_ID"
$ az login --tenant "TENANT_ID_OR_DOMAIN"
```

---

## Configuring Azure CLI authentication in Terraform
If your tenant does not have any subscriptions associated with it, for example if you are administering an Azure Active Directory B2C tenant, you will need to inform the CLI tools to permit signing in without one:

Now that we're logged into the Azure CLI - we can configure Terraform to use these credentials.

To configure Terraform to use the Default Subscription defined in the Azure CLI - we can use the following Provider block:

```hcl
provider "azuread" {
# Whilst version is optional, we /strongly recommend/ using it to pin the version of the Provider being used
version = "=0.1.0"
}
```bash
$ az login --tenant "TENANT_ID_OR_DOMAIN" --allow-no-subscriptions
```

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 Azure CLI to authenticate.

---

It's also possible to configure Terraform to use a specific Subscription - for example:
## Configuring Azure CLI authentication in Terraform

No specific configuration is required for the provider to use Azure CLI authentication. A Provider block is _technically_ optional, however we'd strongly recommend defining one to be able to pin the version of the Provider being used:

```hcl
provider "azuread" {
# Whilst version is optional, we /strongly recommend/ using it to pin the version of the Provider being used
version = "=0.1.0"

subscription_id = "00000000-0000-0000-0000-000000000000"
version = "=0.10.0"
}
```

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 Azure CLI to authenticate.

---

If you're looking to use Terraform across Tenants - it's possible to do this by configuring the Tenant ID field in the Provider block, as shown below:
If you're looking to use Terraform across Tenants - it's possible to do this by configuring the `tenant_id` field in the Provider block, as shown below:

```hcl
provider "azuread" {
# Whilst version is optional, we /strongly recommend/ using it to pin the version of the Provider being used
version = "=0.1.0"
version = "=0.10.0"

subscription_id = "00000000-0000-0000-0000-000000000000"
tenant_id = "11111111-1111-1111-1111-111111111111"
tenant_id = "10000000-2000-3000-4000-500000000000"
}
```

Expand Down
41 changes: 25 additions & 16 deletions website/docs/guides/managed_service_identity.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -20,42 +20,43 @@ Terraform supports a number of different methods for authenticating to Azure:

We recommend using either a Service Principal or Managed Service 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.

## What is Managed Service Identity?

Certain services within Azure (for example Virtual Machines and Virtual Machine Scale Sets) can be assigned an Azure Active Directory identity which can be used to access the Azure Subscription. This identity can then be assigned permissions to a Subscription, Resource Group or other resources using the Azure Identity and Access Management functionality - however by default no permissions are assigned.
Certain services within Azure (for example Virtual Machines and Virtual Machine Scale Sets) can be assigned an Azure Active Directory identity. This identity can then be granted permissions to manage objects in Azure Active Directory.

Once a resource is configured with an identity, a local metadata service exposes credentials which can be used by applications such as Terraform.

## Configuring Managed Service Identity

The (simplified) Terraform Configuration below configures a Virtual Machine with Managed Service Identity, and then grants it Contributor access to the Subscription:
The (simplified) Terraform Configuration below configures a Virtual Machine with Managed Service Identity, and then outputs the Object ID of the corresponding Service Principal:

```hcl
data "azuread_subscription" "current" {}
data "azurerm_subscription" "current" {}

resource "azurerm_linux_virtual_machine" "test" {
name = "test-vm"

resource "azuread_virtual_machine" "test" {
# ...

identity = {
type = "SystemAssigned"
}
}

data "azuread_builtin_role_definition" "contributor" {
name = "Contributor"
}

resource "azuread_role_assignment" "test" {
name = "${azuread_virtual_machine.test.name}"
scope = "${data.azuread_subscription.primary.id}"
role_definition_id = "${data.azuread_subscription.subscription.id}${data.azuread_builtin_role_definition.contributor.id}"
principal_id = "${lookup(azuread_virtual_machine.test.identity[0], "principal_id")}"
output "test_msi_object_id" {
value = azurerm_linux_virtual_machine.test.identity.0.principal_id
}
```

The implicitly created Service Principal should have the same or similar name as your virtual machine.

Refer to the [azurerm_linux_virtual_machine][azurerm_linux_virtual_machine] and [azurerm_windows_virtual_machine][azurerm_windows_virtual_machine] documentation for more information on how to use these resources to launch a new virtual machine.

## Configuring Managed Service Identity in Terraform

At this point we assume that Managed Service Identity is configured on the resource (e.g. Virtual Machine) being used - and that permissions have been assigned via Azure's Identity and Access Management system.
At this point we assume that Managed Service Identity is configured on the resource (e.g. Virtual Machine) being used - and that you are running Terraform on that resource.

Terraform can be configured to use Managed Service Identity for authentication in one of two ways: using Environment Variables or by defining the fields within the Provider block.

Expand All @@ -72,22 +73,24 @@ Whilst a Provider block is _technically_ optional when using Environment Variabl
```hcl
provider "azuread" {
# Whilst version is optional, we /strongly recommend/ using it to pin the version of the Provider being used
version = "=0.1.0"
version = "=0.10.0"
}
```

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 Managed Service Identity.

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.

---

It's also possible to configure Managed Service Identity within the Provider Block:

```hcl
provider "azuread" {
# Whilst version is optional, we /strongly recommend/ using it to pin the version of the Provider being used
version = "=0.1.0"
version = "=0.10.0"

use_msi = true
}
Expand All @@ -98,3 +101,9 @@ provider "azuread" {
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 Managed Service Identity.

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.


[azurerm_linux_virtual_machine]: https://www.terraform.io/docs/providers/azurerm/r/linux_virtual_machine.html
[azurerm_windows_virtual_machine]: https://www.terraform.io/docs/providers/azurerm/r/windows_virtual_machine.html
Loading