From 0c83f2e1b383337fc387cd4ed731b7e17d98ec6c Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Thu, 23 May 2019 12:51:27 +0200 Subject: [PATCH 1/3] azure cli: verifying we're authenticated as a User Unfortunately when authenticating as a Service Principal not all required information is available As such instead we support authenticating as a Service Principal using the separate auth methods for Client Certificate/Secret authentication. ``` $ go test -v ./authentication/ === RUN TestAzureCLITokenAuth_isApplicable --- PASS: TestAzureCLITokenAuth_isApplicable (0.00s) === RUN TestAzureCLITokenAuth_populateConfig --- PASS: TestAzureCLITokenAuth_populateConfig (0.00s) === RUN TestAzureCLITokenAuth_validate --- PASS: TestAzureCLITokenAuth_validate (0.00s) === RUN TestServicePrincipalClientCertAuth_builder --- PASS: TestServicePrincipalClientCertAuth_builder (0.00s) === RUN TestServicePrincipalClientCertAuth_isApplicable --- PASS: TestServicePrincipalClientCertAuth_isApplicable (0.00s) === RUN TestServicePrincipalClientCertAuth_populateConfig --- PASS: TestServicePrincipalClientCertAuth_populateConfig (0.00s) === RUN TestServicePrincipalClientCertAuth_validate --- PASS: TestServicePrincipalClientCertAuth_validate (0.00s) === RUN TestServicePrincipalClientSecretAuth_builder --- PASS: TestServicePrincipalClientSecretAuth_builder (0.00s) === RUN TestServicePrincipalClientSecretAuth_isApplicable --- PASS: TestServicePrincipalClientSecretAuth_isApplicable (0.00s) === RUN TestServicePrincipalClientSecretAuth_populateConfig --- PASS: TestServicePrincipalClientSecretAuth_populateConfig (0.00s) === RUN TestServicePrincipalClientSecretAuth_validate --- PASS: TestServicePrincipalClientSecretAuth_validate (0.00s) === RUN TestManagedServiceIdentity_builder 2019/05/23 12:49:55 [DEBUG] Using MSI endpoint "https://hello-world" --- PASS: TestManagedServiceIdentity_builder (0.00s) === RUN TestManagedServiceIdentity_isApplicable --- PASS: TestManagedServiceIdentity_isApplicable (0.00s) === RUN TestManagedServiceIdentity_populateConfig --- PASS: TestManagedServiceIdentity_populateConfig (0.00s) === RUN TestManagedServiceIdentity_validate --- PASS: TestManagedServiceIdentity_validate (0.00s) === RUN TestAzureFindValidAccessTokenForTenant_Expired --- PASS: TestAzureFindValidAccessTokenForTenant_Expired (0.00s) === RUN TestAzureFindValidAccessTokenForTenant_ExpiringIn --- PASS: TestAzureFindValidAccessTokenForTenant_ExpiringIn (0.00s) === RUN TestAzureFindValidAccessTokenForTenant_InvalidManagementDomain 2019/05/23 12:49:55 [DEBUG] Resource "https://portal.azure.com/" isn't a management domain --- PASS: TestAzureFindValidAccessTokenForTenant_InvalidManagementDomain (0.00s) === RUN TestAzureFindValidAccessTokenForTenant_DifferentTenant 2019/05/23 12:49:55 [DEBUG] Resource "https://management.core.windows.net/" isn't for the correct Tenant --- PASS: TestAzureFindValidAccessTokenForTenant_DifferentTenant (0.00s) === RUN TestAzureFindValidAccessTokenForTenant_Valid --- PASS: TestAzureFindValidAccessTokenForTenant_Valid (0.00s) === RUN TestAzureFindValidAccessTokenForTenant_NoTokens --- PASS: TestAzureFindValidAccessTokenForTenant_NoTokens (0.00s) === RUN TestAzureCliProfile_populateSubscriptionIdMissing --- PASS: TestAzureCliProfile_populateSubscriptionIdMissing (0.00s) === RUN TestAzureCliProfile_populateSubscriptionIdNoDefault --- PASS: TestAzureCliProfile_populateSubscriptionIdNoDefault (0.00s) === RUN TestAzureCliProfile_populateSubscriptionIdValid --- PASS: TestAzureCliProfile_populateSubscriptionIdValid (0.00s) === RUN TestAzureCliProfile_populateTenantIdEmpty --- PASS: TestAzureCliProfile_populateTenantIdEmpty (0.00s) === RUN TestAzureCliProfile_populateTenantIdMissingSubscription --- PASS: TestAzureCliProfile_populateTenantIdMissingSubscription (0.00s) === RUN TestAzureCliProfile_populateTenantIdValid --- PASS: TestAzureCliProfile_populateTenantIdValid (0.00s) === RUN TestAzureCLIProfileFindDefaultSubscription --- PASS: TestAzureCLIProfileFindDefaultSubscription (0.00s) === RUN TestAzureCLIProfileFindSubscription --- PASS: TestAzureCLIProfileFindSubscription (0.00s) === RUN TestAzureEnvironmentNames --- PASS: TestAzureEnvironmentNames (0.00s) PASS ok github.com/hashicorp/go-azure-helpers/authentication 1.319s ``` --- authentication/auth_method_azure_cli_token.go | 6 ++++++ authentication/azure_cli_profile.go | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/authentication/auth_method_azure_cli_token.go b/authentication/auth_method_azure_cli_token.go index 6f854d5..9c86a3f 100644 --- a/authentication/auth_method_azure_cli_token.go +++ b/authentication/auth_method_azure_cli_token.go @@ -38,6 +38,12 @@ func (a azureCliTokenAuth) build(b Builder) (authMethod, error) { auth.profile.profile = profile + // Authenticating as a Service Principal doesn't return all of the information we need for authentication purposes + // as such Service Principal authentication is supported using the specific auth method + if authenticatedAsAUser := auth.profile.verifyAuthenticatedAsAUser(); !authenticatedAsAUser { + return nil, fmt.Errorf("Authenticating using the Azure CLI is only supported as a User (not a Service Principal)") + } + err = auth.profile.populateFields() if err != nil { return nil, fmt.Errorf("Error retrieving the Profile from the Azure CLI: %s Please re-authenticate using `az login`.", err) diff --git a/authentication/azure_cli_profile.go b/authentication/azure_cli_profile.go index 39fb30d..1876024 100644 --- a/authentication/azure_cli_profile.go +++ b/authentication/azure_cli_profile.go @@ -1,6 +1,8 @@ package authentication import ( + "strings" + "github.com/Azure/go-autorest/autorest/azure/cli" ) @@ -33,3 +35,18 @@ func (a *azureCLIProfile) populateFields() error { // always pull the environment from the Azure CLI, since the Access Token's associated with it return a.populateEnvironment() } + +func (a *azureCLIProfile) verifyAuthenticatedAsAUser() bool { + for _, subscription := range a.profile.Subscriptions { + if subscription.User == nil { + continue + } + + authenticatedAsAUser := strings.EqualFold(subscription.User.Type, "user") + if authenticatedAsAUser { + return true + } + } + + return false +} \ No newline at end of file From 9393cd0b2214868bdf3c322b6c461f06aa4879cb Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Mon, 15 Jul 2019 09:27:47 +0100 Subject: [PATCH 2/3] f/azurecli-auth: making the error message more helpful --- authentication/auth_method_azure_cli_token.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/authentication/auth_method_azure_cli_token.go b/authentication/auth_method_azure_cli_token.go index 9c86a3f..775a2f5 100644 --- a/authentication/auth_method_azure_cli_token.go +++ b/authentication/auth_method_azure_cli_token.go @@ -41,7 +41,12 @@ func (a azureCliTokenAuth) build(b Builder) (authMethod, error) { // Authenticating as a Service Principal doesn't return all of the information we need for authentication purposes // as such Service Principal authentication is supported using the specific auth method if authenticatedAsAUser := auth.profile.verifyAuthenticatedAsAUser(); !authenticatedAsAUser { - return nil, fmt.Errorf("Authenticating using the Azure CLI is only supported as a User (not a Service Principal)") + return nil, fmt.Errorf(`Authenticating using the Azure CLI is only supported as a User (not a Service Principal). + +To authenticate to Azure using a Service Principal, you can use the separate 'Authenticate using a Service Principal' +auth method - instructions for which can be found in the documentation. + +Alternatively you can authenticate using the Azure CLI by using a User Account.`) } err = auth.profile.populateFields() From a8d090bdf7c19a36ece5b570ecc5e8f390b627be Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Mon, 15 Jul 2019 09:33:23 +0100 Subject: [PATCH 3/3] f/azurecli-auth: exposing the provider specific docs uri --- authentication/auth_method_azure_cli_token.go | 6 ++++-- authentication/builder.go | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/authentication/auth_method_azure_cli_token.go b/authentication/auth_method_azure_cli_token.go index 775a2f5..184037d 100644 --- a/authentication/auth_method_azure_cli_token.go +++ b/authentication/auth_method_azure_cli_token.go @@ -15,6 +15,7 @@ import ( type azureCliTokenAuth struct { profile *azureCLIProfile + servicePrincipalAuthDocsLink string } func (a azureCliTokenAuth) build(b Builder) (authMethod, error) { @@ -25,6 +26,7 @@ func (a azureCliTokenAuth) build(b Builder) (authMethod, error) { subscriptionId: b.SubscriptionID, tenantId: b.TenantID, }, + servicePrincipalAuthDocsLink: b.ClientSecretDocsLink, } profilePath, err := cli.ProfilePath() if err != nil { @@ -44,9 +46,9 @@ func (a azureCliTokenAuth) build(b Builder) (authMethod, error) { return nil, fmt.Errorf(`Authenticating using the Azure CLI is only supported as a User (not a Service Principal). To authenticate to Azure using a Service Principal, you can use the separate 'Authenticate using a Service Principal' -auth method - instructions for which can be found in the documentation. +auth method - instructions for which can be found here: %s -Alternatively you can authenticate using the Azure CLI by using a User Account.`) +Alternatively you can authenticate using the Azure CLI by using a User Account.`, auth.servicePrincipalAuthDocsLink) } err = auth.profile.populateFields() diff --git a/authentication/builder.go b/authentication/builder.go index e37e8b1..8a187c6 100644 --- a/authentication/builder.go +++ b/authentication/builder.go @@ -33,6 +33,7 @@ type Builder struct { // Service Principal (Client Secret) Auth SupportsClientSecretAuth bool ClientSecret string + ClientSecretDocsLink string } // Build takes the configuration from the Builder and builds up a validated Config