Skip to content

Add partner_id setting to provider #781

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

Merged
Merged
Changes from 5 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
5 changes: 5 additions & 0 deletions .changes/unreleased/added-20250517-062944.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: added
body: support partner_id configuration and opt-out for default Terraform partner ID
time: 2025-05-17T06:29:44.020285595Z
custom:
Issue: "781"
6 changes: 6 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -358,6 +358,10 @@ We recommend using Environment Variables to pass the credentials to the provider
| `POWER_PLATFORM_CLIENT_CERTIFICATE` | The Base64 format of your certificate that will be used for certificate-based authentication | |
| `POWER_PLATFORM_CLIENT_CERTIFICATE_FILE_PATH` | The path to the certificate that will be used for certificate-based authentication | |
| `POWER_PLATFORM_AZDO_SERVICE_CONNECTION_ID` | The GUID of the Azure DevOps service connection to be used for Azure DevOps Workload Identity Federation | |
| `POWER_PLATFORM_PARTNER_ID` | Partner GUID used for Customer Usage Attribution. | |
| `POWER_PLATFORM_DISABLE_TERRAFORM_PARTNER_ID` | If set to `true`, the default Terraform partner ID will not be sent. | |
| `ARM_PARTNER_ID` | Alternative environment variable for the partner GUID. | |
| `ARM_DISABLE_TERRAFORM_PARTNER_ID` | Alternative variable to disable the default Terraform partner ID. | |

-> Variables passed into the provider will override the environment variables.

@@ -381,6 +385,8 @@ In addition to the authentication options, the following options are also suppor
| Name | Description | Default Value |
|------|-------------|---------------|
| `telemetry_optout` | Opting out of telemetry will remove the User-Agent and session id headers from the requests made to the Power Platform service. There is no other telemetry data collected by the provider. This may affect the ability to identify and troubleshoot issues with the provider. | `false` |
| `partner_id` | Optional GUID for Customer Usage Attribution. When set, the value is appended to the User-Agent header as `pid-<GUID>`. | |
| `disable_terraform_partner_id` | When `true`, suppresses the default Terraform partner ID when no custom `partner_id` is provided. | `false` |


If you are using Azure CLI for authentication, you can also turn off CLI's telemetry by executing the following [command](https://github.com/Azure/azure-cli?tab=readme-ov-file#telemetry-configuration):
13 changes: 13 additions & 0 deletions examples/provider/provider_with_partner_id.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
terraform {
required_providers {
powerplatform = {
source = "microsoft/power-platform"
}
}
}

# Provider configuration including partner ID for Customer Usage Attribution
provider "powerplatform" {
use_cli = true
partner_id = "00000000-0000-0000-0000-000000000000"
}
6 changes: 6 additions & 0 deletions internal/api/request.go
Original file line number Diff line number Diff line change
@@ -146,6 +146,12 @@ func (client *Client) buildCorrelationHeaders(ctx context.Context) (sessionId st
func (client *Client) buildUserAgent(ctx context.Context) string {
userAgent := fmt.Sprintf("terraform-provider-power-platform/%s (%s; %s) terraform/%s go/%s", common.ProviderVersion, runtime.GOOS, runtime.GOARCH, client.Config.TerraformVersion, runtime.Version())

if client.Config.PartnerId != "" {
userAgent += fmt.Sprintf(" pid-%s", client.Config.PartnerId)
} else if !client.Config.DisableTerraformPartnerId {
userAgent += fmt.Sprintf(" pid-%s", constants.DEFAULT_TERRAFORM_PARTNER_ID)
}

requestContext, ok := ctx.Value(helpers.REQUEST_CONTEXT_KEY).(helpers.RequestContextValue)
if ok {
userAgent += fmt.Sprintf(" %s %s", requestContext.ObjectName, requestContext.RequestType)
16 changes: 16 additions & 0 deletions internal/api/request_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package api

import (
"context"
"testing"

"github.com/microsoft/terraform-provider-power-platform/internal/config"
"github.com/stretchr/testify/require"
)

func TestUnitBuildUserAgent_WithPartnerId(t *testing.T) {
cfg := config.ProviderConfig{PartnerId: "00000000-0000-0000-0000-000000000001"}
client := NewApiClientBase(&cfg, NewAuthBase(&cfg))
ua := client.buildUserAgent(context.Background())
require.Contains(t, ua, "pid-00000000-0000-0000-0000-000000000001")
}
19 changes: 12 additions & 7 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ package config
import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/microsoft/terraform-provider-power-platform/internal/customtypes"
"github.com/microsoft/terraform-provider-power-platform/internal/helpers"
)

@@ -52,11 +53,13 @@ type ProviderConfig struct {
EnableContinuousAccessEvaluation bool

// internal runtime configuration values
TestMode bool
Urls ProviderConfigUrls
TelemetryOptout bool
Cloud cloud.Configuration
TerraformVersion string
TestMode bool
Urls ProviderConfigUrls
TelemetryOptout bool
PartnerId string
DisableTerraformPartnerId bool
Cloud cloud.Configuration
TerraformVersion string
}

type ProviderConfigUrls struct {
@@ -135,8 +138,10 @@ type ProviderConfigModel struct {
UseOidc types.Bool `tfsdk:"use_oidc"`
UseMsi types.Bool `tfsdk:"use_msi"`

Cloud types.String `tfsdk:"cloud"`
TelemetryOptout types.Bool `tfsdk:"telemetry_optout"`
Cloud types.String `tfsdk:"cloud"`
TelemetryOptout types.Bool `tfsdk:"telemetry_optout"`
PartnerId customtypes.UUID `tfsdk:"partner_id"`
DisableTerraformPartnerId types.Bool `tfsdk:"disable_terraform_partner_id"`

TenantId types.String `tfsdk:"tenant_id"`
AuxiliaryTenantIDs types.List `tfsdk:"auxiliary_tenant_ids"`
7 changes: 7 additions & 0 deletions internal/constants/constants.go
Original file line number Diff line number Diff line change
@@ -135,6 +135,8 @@ const (
HEADER_RETRY_AFTER = "Retry-After"
HTTPS = "https"
API_VERSION_PARAM = "api-version"

DEFAULT_TERRAFORM_PARTNER_ID = "222c6c49-1b0a-5959-a213-6608f9eb8820"
)

const (
@@ -164,6 +166,11 @@ const (
ENV_VAR_ARM_OIDC_TOKEN = "ARM_OIDC_TOKEN"
ENV_VAR_ARM_OIDC_TOKEN_FILE_PATH = "ARM_OIDC_TOKEN_FILE_PATH"
ENV_VAR_ARM_AUXILIARY_TENANT_IDS = "ARM_AUXILIARY_TENANT_IDS"

ENV_VAR_POWER_PLATFORM_PARTNER_ID = "POWER_PLATFORM_PARTNER_ID"
ENV_VAR_ARM_PARTNER_ID = "ARM_PARTNER_ID"
ENV_VAR_POWER_PLATFORM_DISABLE_TERRAFORM_PARTNER_ID = "POWER_PLATFORM_DISABLE_TERRAFORM_PARTNER_ID"
ENV_VAR_ARM_DISABLE_TERRAFORM_PARTNER_ID = "ARM_DISABLE_TERRAFORM_PARTNER_ID"
)

const (
38 changes: 34 additions & 4 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
import (
"context"
"fmt"
"github.com/google/uuid"
"os"

"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
@@ -63,10 +64,12 @@
func NewPowerPlatformProvider(ctx context.Context, testModeEnabled ...bool) func() provider.Provider {
cloudUrls, cloudConfig := getCloudPublicUrls()
providerConfig := config.ProviderConfig{
Urls: *cloudUrls,
Cloud: *cloudConfig,
TerraformVersion: "unknown",
TelemetryOptout: false,
Urls: *cloudUrls,
Cloud: *cloudConfig,
TerraformVersion: "unknown",
TelemetryOptout: false,
PartnerId: DEFAULT_TERRAFORM_PARTNER_ID,

Check failure on line 71 in internal/provider/provider.go

GitHub Actions / lint

undefined: DEFAULT_TERRAFORM_PARTNER_ID) (typecheck)

Check failure on line 71 in internal/provider/provider.go

GitHub Actions / lint

undefined: DEFAULT_TERRAFORM_PARTNER_ID) (typecheck)

Check failure on line 71 in internal/provider/provider.go

GitHub Actions / lint

undefined: DEFAULT_TERRAFORM_PARTNER_ID (typecheck)

Check failure on line 71 in internal/provider/provider.go

GitHub Actions / lint

undefined: DEFAULT_TERRAFORM_PARTNER_ID) (typecheck)

Check failure on line 71 in internal/provider/provider.go

GitHub Actions / Build

undefined: DEFAULT_TERRAFORM_PARTNER_ID
DisableTerraformPartnerId: false,
}

if len(testModeEnabled) > 0 && testModeEnabled[0] {
@@ -166,6 +169,15 @@
MarkdownDescription: "Flag to indicate whether to opt out of telemetry. Default is `false`",
Optional: true,
},
"partner_id": schema.StringAttribute{
MarkdownDescription: "The GUID of the partner for Customer Usage Attribution (CUA).",
Optional: true,
CustomType: customtypes.UUIDType{},
},
"disable_terraform_partner_id": schema.BoolAttribute{
MarkdownDescription: "Disable sending the default Terraform partner ID when no custom partner_id is provided. Default is `false`",
Optional: true,
},
"use_msi": schema.BoolAttribute{
MarkdownDescription: "Flag to indicate whether to use managed identity for authentication",
Optional: true,
@@ -216,6 +228,22 @@
// Check for telemetry opt out
telemetryOptOut := helpers.GetConfigBool(ctx, configValue.TelemetryOptout, constants.ENV_VAR_POWER_PLATFORM_TELEMETRY_OPTOUT, false)

partnerId := helpers.GetConfigMultiString(ctx, configValue.PartnerId.StringValue, []string{constants.ENV_VAR_POWER_PLATFORM_PARTNER_ID, constants.ENV_VAR_ARM_PARTNER_ID}, "")
if partnerId != "" {
if _, err := uuid.Parse(partnerId); err != nil {
resp.Diagnostics.AddAttributeError(
path.Root("partner_id"),
"Invalid UUID",
fmt.Sprintf("The partner_id must be a valid UUID: %v", err),
)
return
}
}
disableTerraformPartnerId := helpers.GetConfigBool(ctx, configValue.DisableTerraformPartnerId, constants.ENV_VAR_POWER_PLATFORM_DISABLE_TERRAFORM_PARTNER_ID, false)
if !disableTerraformPartnerId {
disableTerraformPartnerId = helpers.GetConfigBool(ctx, types.BoolNull(), constants.ENV_VAR_ARM_DISABLE_TERRAFORM_PARTNER_ID, false)
}

// Get CAE configuration
enableCae := helpers.GetConfigBool(ctx, configValue.EnableContinuousAccessEvaluation, constants.ENV_VAR_POWER_PLATFORM_ENABLE_CAE, false)

@@ -262,6 +290,8 @@
p.Config.Urls = *providerConfigUrls
p.Config.Cloud = *cloudConfiguration
p.Config.TelemetryOptout = telemetryOptOut
p.Config.PartnerId = partnerId
p.Config.DisableTerraformPartnerId = disableTerraformPartnerId
p.Config.EnableContinuousAccessEvaluation = enableCae
p.Config.TerraformVersion = req.TerraformVersion

42 changes: 42 additions & 0 deletions internal/provider/provider_test.go
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@
test "github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/jarcoal/httpmock"
"github.com/microsoft/terraform-provider-power-platform/internal/mocks"
"github.com/microsoft/terraform-provider-power-platform/internal/provider"

Check failure on line 17 in internal/provider/provider_test.go

GitHub Actions / lint

could not import github.com/microsoft/terraform-provider-power-platform/internal/provider (-: # github.com/microsoft/terraform-provider-power-platform/internal/provider
"github.com/microsoft/terraform-provider-power-platform/internal/services/admin_management_application"
"github.com/microsoft/terraform-provider-power-platform/internal/services/analytics_data_export"
"github.com/microsoft/terraform-provider-power-platform/internal/services/application"
@@ -175,3 +175,45 @@
},
})
}

func TestUnitPowerPlatformProvider_PartnerId_Valid(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

mocks.ActivateEnvironmentHttpMocks()

httpmock.RegisterResponder("GET", `https://api.bap.microsoft.com/providers/Microsoft.BusinessAppPlatform/scopes/admin/environments?%24expand=properties%2FbillingPolicy%2Cproperties%2FcopilotPolicies&api-version=2023-06-01`,
func(req *http.Request) (*http.Response, error) {
return httpmock.NewStringResponse(http.StatusOK, httpmock.File("../services/environment/tests/datasource/Validate_Read/get_environments.json").String()), nil
})

test.Test(t, test.TestCase{
IsUnitTest: true,
ProtoV6ProviderFactories: mocks.TestUnitTestProtoV6ProviderFactories,
Steps: []test.TestStep{
{
Config: `provider "powerplatform" {
use_cli = true
partner_id = "00000000-0000-0000-0000-000000000001"
}
data "powerplatform_environments" "all" {}`,
},
},
})
}

func TestUnitPowerPlatformProvider_PartnerId_Invalid(t *testing.T) {
test.Test(t, test.TestCase{
IsUnitTest: true,
ProtoV6ProviderFactories: mocks.TestUnitTestProtoV6ProviderFactories,
Steps: []test.TestStep{
{
Config: `provider "powerplatform" {
partner_id = "invalid-guid"
}
data "powerplatform_environments" "all" {}`,
ExpectError: regexp.MustCompile("Invalid UUID"),
},
},
})
}
6 changes: 6 additions & 0 deletions templates/index.md.tmpl
Original file line number Diff line number Diff line change
@@ -358,6 +358,10 @@ We recommend using Environment Variables to pass the credentials to the provider
| `POWER_PLATFORM_CLIENT_CERTIFICATE` | The Base64 format of your certificate that will be used for certificate-based authentication | |
| `POWER_PLATFORM_CLIENT_CERTIFICATE_FILE_PATH` | The path to the certificate that will be used for certificate-based authentication | |
| `POWER_PLATFORM_AZDO_SERVICE_CONNECTION_ID` | The GUID of the Azure DevOps service connection to be used for Azure DevOps Workload Identity Federation | |
| `POWER_PLATFORM_PARTNER_ID` | Partner GUID used for Customer Usage Attribution. | |
| `POWER_PLATFORM_DISABLE_TERRAFORM_PARTNER_ID` | If set to `true`, the default Terraform partner ID will not be sent. | |
| `ARM_PARTNER_ID` | Alternative environment variable for the partner GUID. | |
| `ARM_DISABLE_TERRAFORM_PARTNER_ID` | Alternative variable to disable the default Terraform partner ID. | |

-> Variables passed into the provider will override the environment variables.

@@ -381,6 +385,8 @@ In addition to the authentication options, the following options are also suppor
| Name | Description | Default Value |
|------|-------------|---------------|
| `telemetry_optout` | Opting out of telemetry will remove the User-Agent and session id headers from the requests made to the Power Platform service. There is no other telemetry data collected by the provider. This may affect the ability to identify and troubleshoot issues with the provider. | `false` |
| `partner_id` | Optional GUID for Customer Usage Attribution. When set, the value is appended to the User-Agent header as `pid-<GUID>`. | |
| `disable_terraform_partner_id` | When `true`, suppresses the default Terraform partner ID when no custom `partner_id` is provided. | `false` |


If you are using Azure CLI for authentication, you can also turn off CLI's telemetry by executing the following [command](https://github.com/Azure/azure-cli?tab=readme-ov-file#telemetry-configuration):
Loading
Oops, something went wrong.