mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-22 03:07:51 -05:00
118 lines
3.4 KiB
Go
118 lines
3.4 KiB
Go
// Copyright (c) The OpenTofu Authors
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright (c) 2023 HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package auth
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os/exec"
|
|
|
|
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
|
"github.com/opentofu/opentofu/internal/tfdiags"
|
|
)
|
|
|
|
type AzureCLIAuthConfig struct {
|
|
CLIAuthEnabled bool
|
|
}
|
|
|
|
type azureCLICredentialAuth struct{}
|
|
|
|
var _ AuthMethod = &azureCLICredentialAuth{}
|
|
|
|
func (cred *azureCLICredentialAuth) Name() string {
|
|
return "Azure CLI Auth"
|
|
}
|
|
|
|
func (cred *azureCLICredentialAuth) Construct(_ context.Context, config *Config) (azcore.TokenCredential, error) {
|
|
// The SubscriptionID and TenantID can be empty, and the logic of this will still be okay
|
|
return azidentity.NewAzureCLICredential(&azidentity.AzureCLICredentialOptions{
|
|
Subscription: config.StorageAddresses.SubscriptionID,
|
|
TenantID: config.StorageAddresses.TenantID,
|
|
})
|
|
}
|
|
|
|
func (cred *azureCLICredentialAuth) Validate(ctx context.Context, config *Config) tfdiags.Diagnostics {
|
|
var diags tfdiags.Diagnostics
|
|
if !config.CLIAuthEnabled {
|
|
diags = diags.Append(tfdiags.Sourceless(
|
|
tfdiags.Error,
|
|
"Invalid Azure Command Line Auth",
|
|
"Setting use_cli to false prevents the use of command-line auth (az).",
|
|
))
|
|
return diags
|
|
}
|
|
_, err := exec.LookPath("az")
|
|
if err != nil {
|
|
diags = diags.Append(tfdiags.Sourceless(
|
|
tfdiags.Error,
|
|
"Invalid Azure Command Line Auth",
|
|
"Error looking for command az in your PATH. Make sure the Azure Command Line tool is installed and executable.",
|
|
))
|
|
return diags
|
|
}
|
|
// Make sure the user is logged in by attempting to get the subscription
|
|
_, err = getCurrentSubscriptionInfo(ctx)
|
|
if err != nil {
|
|
diags = diags.Append(tfdiags.Sourceless(
|
|
tfdiags.Error,
|
|
"Invalid Azure Command Line Auth",
|
|
fmt.Sprintf("Error using the az command: %s.", tfdiags.FormatError(err)),
|
|
))
|
|
}
|
|
return diags
|
|
}
|
|
|
|
func (cred *azureCLICredentialAuth) AugmentConfig(ctx context.Context, config *Config) (err error) {
|
|
if config.StorageAddresses.SubscriptionID == "" {
|
|
config.StorageAddresses.SubscriptionID, err = getCliAzureSubscriptionID(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return checkNamesForAccessKeyCredentials(config.StorageAddresses)
|
|
}
|
|
|
|
type Subscription struct {
|
|
Id string `json:"id"`
|
|
Name string `json:"name"`
|
|
IsDefault bool `json:"isDefault"`
|
|
}
|
|
|
|
// getCliAzureSubscriptionID obtains the subscription ID currently active in the
|
|
// Azure profile. This assumes the user has the Azure CLI installed on their machine.
|
|
func getCliAzureSubscriptionID(ctx context.Context) (string, error) {
|
|
rawSubscription, err := getCurrentSubscriptionInfo(ctx)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
var subscription Subscription
|
|
err = json.Unmarshal(rawSubscription, &subscription)
|
|
if err != nil {
|
|
return "", fmt.Errorf("json error for azure subscription: %w", err)
|
|
}
|
|
|
|
return subscription.Id, nil
|
|
}
|
|
|
|
// getCurrentSubscriptionInfo is adapted from azure-sdk-for-go's CLI token retrieval
|
|
func getCurrentSubscriptionInfo(ctx context.Context) ([]byte, error) {
|
|
cliCmd := exec.CommandContext(ctx, "az", "account", "show", "-o", "json")
|
|
var stderr bytes.Buffer
|
|
cliCmd.Stderr = &stderr
|
|
|
|
stdout, err := cliCmd.Output()
|
|
if err != nil {
|
|
msg := stderr.String()
|
|
return nil, fmt.Errorf("error getting subscription info: error: %w\nmore information: %s", err, msg)
|
|
}
|
|
|
|
return stdout, nil
|
|
}
|