Skip to content

Commit e53a2e4

Browse files
authored
new data source: azapi_client_config (#521)
1 parent b255156 commit e53a2e4

File tree

8 files changed

+241
-19
lines changed

8 files changed

+241
-19
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
## v1.14.0 (unreleased)
2+
FEATURES:
3+
- **New Data Source**: azapi_client_config
24

35
ENHANCEMENTS:
46
- `azapi` provider: Support `client_certificate` field, which specifies base64-encoded PKCS#12 bundle to be used as the client certificate for authentication.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
subcategory: ""
3+
layout: "azapi"
4+
page_title: "Client Config Data Source: azapi_client_config"
5+
description: |-
6+
Gets information about the configuration of the azapi provider.
7+
---
8+
9+
# azapi_client_config
10+
11+
Use this data source to access the configuration of the azapi provider.
12+
13+
## Example Usage
14+
15+
```hcl
16+
terraform {
17+
required_providers {
18+
azapi = {
19+
source = "Azure/azapi"
20+
}
21+
}
22+
}
23+
24+
provider "azapi" {
25+
}
26+
27+
data "azapi_client_config" "current" {
28+
}
29+
30+
output "subscription_id" {
31+
value = data.azapi_client_config.current.subscription_id
32+
}
33+
34+
output "tenant_id" {
35+
value = data.azapi_client_config.current.tenant_id
36+
}
37+
```
38+
39+
## Arguments Reference
40+
41+
There are no arguments available for this data source.
42+
43+
## Attributes Reference
44+
45+
* `tenant_id` is set to the Azure Tenant ID.
46+
47+
* `subscription_id` is set to the Azure Subscription ID.
48+
49+
## Timeouts
50+
51+
The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions:
52+
53+
* `read` - (Defaults to 30 minutes) Used when retrieving the azure resource.

internal/acceptance/testclient.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ func BuildTestClient() (*clients.Client, error) {
5353
CloudCfg: cloudConfig,
5454
Features: features.Default(),
5555
SkipProviderRegistration: true,
56+
TenantId: os.Getenv("ARM_TENANT_ID"),
57+
SubscriptionId: os.Getenv("ARM_SUBSCRIPTION_ID"),
5658
}
5759

5860
client := &clients.Client{}

internal/clients/account.go

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,50 +11,67 @@ import (
1111
)
1212

1313
type ResourceManagerAccount struct {
14+
tenantId *string
1415
subscriptionId *string
1516
mutex *sync.Mutex
1617
}
1718

18-
func NewResourceManagerAccount(subscriptionId string) ResourceManagerAccount {
19-
if subscriptionId == "" {
20-
return ResourceManagerAccount{
21-
mutex: &sync.Mutex{},
22-
}
19+
func NewResourceManagerAccount(tenantId, subscriptionId string) ResourceManagerAccount {
20+
out := ResourceManagerAccount{
21+
mutex: &sync.Mutex{},
22+
}
23+
if tenantId != "" {
24+
out.tenantId = &tenantId
2325
}
24-
return ResourceManagerAccount{
25-
subscriptionId: &subscriptionId,
26-
mutex: &sync.Mutex{},
26+
if subscriptionId != "" {
27+
out.subscriptionId = &subscriptionId
2728
}
29+
return out
2830
}
2931

30-
func (account ResourceManagerAccount) GetSubscriptionId() string {
32+
func (account *ResourceManagerAccount) GetTenantId() string {
33+
account.mutex.Lock()
34+
defer account.mutex.Unlock()
35+
if account.tenantId != nil {
36+
return *account.tenantId
37+
}
38+
39+
err := account.loadDefaultsFromAzCmd()
40+
if err != nil {
41+
log.Printf("[DEBUG] Error getting default tenant ID: %s", err)
42+
}
43+
44+
return *account.tenantId
45+
}
46+
47+
func (account *ResourceManagerAccount) GetSubscriptionId() string {
3148
account.mutex.Lock()
3249
defer account.mutex.Unlock()
3350
if account.subscriptionId != nil {
3451
return *account.subscriptionId
3552
}
3653

37-
subscriptionId, err := getDefaultSubscriptionID()
54+
err := account.loadDefaultsFromAzCmd()
3855
if err != nil {
3956
log.Printf("[DEBUG] Error getting default subscription ID: %s", err)
4057
}
4158

42-
account.subscriptionId = &subscriptionId
43-
4459
return *account.subscriptionId
4560
}
4661

47-
// getDefaultSubscriptionID tries to determine the default subscription
48-
func getDefaultSubscriptionID() (string, error) {
49-
var account struct {
62+
func (account *ResourceManagerAccount) loadDefaultsFromAzCmd() error {
63+
var accountModel struct {
5064
SubscriptionID string `json:"id"`
65+
TenantId string `json:"tenantId"`
5166
}
52-
err := jsonUnmarshalAzCmd(&account, "account", "show")
67+
err := jsonUnmarshalAzCmd(&accountModel, "account", "show")
5368
if err != nil {
54-
return "", fmt.Errorf("obtaining subscription ID: %s", err)
69+
return fmt.Errorf("obtaining defaults from az cmd: %s", err)
5570
}
5671

57-
return account.SubscriptionID, nil
72+
account.tenantId = &accountModel.TenantId
73+
account.subscriptionId = &accountModel.SubscriptionID
74+
return nil
5875
}
5976

6077
// jsonUnmarshalAzCmd executes an Azure CLI command and unmarshalls the JSON output.

internal/clients/client.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type Option struct {
3434
CloudCfg cloud.Configuration
3535
CustomCorrelationRequestID string
3636
SubscriptionId string
37+
TenantId string
3738
}
3839

3940
// NOTE: it should be possible for this method to become Private once the top level Client's removed
@@ -130,7 +131,7 @@ func (client *Client) Build(ctx context.Context, o *Option) error {
130131
}
131132
client.DataPlaneClient = dataPlaneClient
132133

133-
client.Account = NewResourceManagerAccount(o.SubscriptionId)
134+
client.Account = NewResourceManagerAccount(o.TenantId, o.SubscriptionId)
134135

135136
return nil
136137
}

internal/provider/provider.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,7 @@ func (p Provider) Configure(ctx context.Context, request provider.ConfigureReque
609609
DisableCorrelationRequestID: model.DisableCorrelationRequestID.ValueBool(),
610610
CustomCorrelationRequestID: model.CustomCorrelationRequestID.ValueString(),
611611
SubscriptionId: model.SubscriptionID.ValueString(),
612+
TenantId: model.TenantID.ValueString(),
612613
}
613614

614615
client := &clients.Client{}
@@ -638,6 +639,9 @@ func (p Provider) DataSources(ctx context.Context) []func() datasource.DataSourc
638639
func() datasource.DataSource {
639640
return &services.AzapiResourceDataSource{}
640641
},
642+
func() datasource.DataSource {
643+
return &services.ClientConfigDataSource{}
644+
},
641645
}
642646

643647
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package services
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
7+
8+
"github.com/Azure/terraform-provider-azapi/internal/clients"
9+
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
10+
"github.com/hashicorp/terraform-plugin-framework/datasource"
11+
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
12+
"github.com/hashicorp/terraform-plugin-framework/types"
13+
)
14+
15+
type ClientConfigDataSourceModel struct {
16+
ID types.String `tfsdk:"id"`
17+
TenantID types.String `tfsdk:"tenant_id"`
18+
SubscriptionID types.String `tfsdk:"subscription_id"`
19+
Timeouts timeouts.Value `tfsdk:"timeouts"`
20+
}
21+
22+
type ClientConfigDataSource struct {
23+
ProviderData *clients.Client
24+
}
25+
26+
var _ datasource.DataSource = &ClientConfigDataSource{}
27+
var _ datasource.DataSourceWithConfigure = &ClientConfigDataSource{}
28+
29+
func (r *ClientConfigDataSource) Configure(ctx context.Context, request datasource.ConfigureRequest, response *datasource.ConfigureResponse) {
30+
if v, ok := request.ProviderData.(*clients.Client); ok {
31+
r.ProviderData = v
32+
}
33+
}
34+
35+
func (r *ClientConfigDataSource) Metadata(ctx context.Context, request datasource.MetadataRequest, response *datasource.MetadataResponse) {
36+
response.TypeName = request.ProviderTypeName + "_client_config"
37+
}
38+
39+
func (r *ClientConfigDataSource) Schema(ctx context.Context, request datasource.SchemaRequest, response *datasource.SchemaResponse) {
40+
response.Schema = schema.Schema{
41+
Attributes: map[string]schema.Attribute{
42+
"id": schema.StringAttribute{
43+
Computed: true,
44+
},
45+
46+
"tenant_id": schema.StringAttribute{
47+
Computed: true,
48+
},
49+
50+
"subscription_id": schema.StringAttribute{
51+
Computed: true,
52+
},
53+
},
54+
55+
Blocks: map[string]schema.Block{
56+
"timeouts": timeouts.Block(ctx, timeouts.Opts{
57+
Read: true,
58+
}),
59+
},
60+
}
61+
}
62+
63+
func (r *ClientConfigDataSource) Read(ctx context.Context, request datasource.ReadRequest, response *datasource.ReadResponse) {
64+
var model ClientConfigDataSourceModel
65+
if response.Diagnostics.Append(request.Config.Get(ctx, &model)...); response.Diagnostics.HasError() {
66+
return
67+
}
68+
69+
readTimeout, diags := model.Timeouts.Read(ctx, 5*time.Minute)
70+
response.Diagnostics.Append(diags...)
71+
if response.Diagnostics.HasError() {
72+
return
73+
}
74+
75+
ctx, cancel := context.WithTimeout(ctx, readTimeout)
76+
defer cancel()
77+
78+
subscriptionId := r.ProviderData.Account.GetSubscriptionId()
79+
tenantId := r.ProviderData.Account.GetTenantId()
80+
81+
model.ID = types.StringValue(fmt.Sprintf("clientConfigs/subscriptionId=%s;tenantId=%s", subscriptionId, tenantId))
82+
model.SubscriptionID = types.StringValue(subscriptionId)
83+
model.TenantID = types.StringValue(tenantId)
84+
response.Diagnostics.Append(response.State.Set(ctx, &model)...)
85+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package services_test
2+
3+
import (
4+
"os"
5+
"regexp"
6+
"testing"
7+
8+
"github.com/Azure/terraform-provider-azapi/internal/acceptance"
9+
"github.com/Azure/terraform-provider-azapi/internal/acceptance/check"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
11+
)
12+
13+
type ClientConfigDataSource struct{}
14+
15+
func TestAccClientConfigDataSource_basic(t *testing.T) {
16+
data := acceptance.BuildTestData(t, "data.azapi_client_config", "test")
17+
r := ClientConfigDataSource{}
18+
19+
tenantId := os.Getenv("ARM_TENANT_ID")
20+
subscriptionId := os.Getenv("ARM_SUBSCRIPTION_ID")
21+
22+
data.DataSourceTest(t, []resource.TestStep{
23+
{
24+
Config: r.basic(),
25+
Check: resource.ComposeTestCheckFunc(
26+
check.That(data.ResourceName).Key("tenant_id").HasValue(tenantId),
27+
check.That(data.ResourceName).Key("subscription_id").HasValue(subscriptionId),
28+
),
29+
},
30+
})
31+
}
32+
33+
func TestAccClientConfigDataSource_azcli(t *testing.T) {
34+
if ok := os.Getenv("ARM_USE_CLI"); ok == "" {
35+
t.Skip("Skipping as `ARM_USE_CLI` is not specified")
36+
}
37+
38+
data := acceptance.BuildTestData(t, "data.azapi_client_config", "test")
39+
r := ClientConfigDataSource{}
40+
41+
idRegex := regexp.MustCompile("^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$")
42+
43+
data.DataSourceTest(t, []resource.TestStep{
44+
{
45+
Config: r.basic(),
46+
Check: resource.ComposeTestCheckFunc(
47+
check.That(data.ResourceName).Key("tenant_id").MatchesRegex(idRegex),
48+
check.That(data.ResourceName).Key("subscription_id").MatchesRegex(idRegex),
49+
),
50+
},
51+
})
52+
}
53+
54+
func (r ClientConfigDataSource) basic() string {
55+
return `
56+
data "azapi_client_config" "test" {}
57+
`
58+
}

0 commit comments

Comments
 (0)