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

passwordless sqlcmd sqlOperation #4140

Closed
wants to merge 13 commits into from
3 changes: 2 additions & 1 deletion cli/azd/.vscode/cspell-azd-dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ servicebus
setenvs
snapshotter
springapp
sqlcmd
sqlserver
sstore
staticcheck
Expand Down Expand Up @@ -216,4 +217,4 @@ webfrontend
westus2
wireinject
yacspin
zerr
zerr
2 changes: 2 additions & 0 deletions cli/azd/cmd/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import (
"github.com/azure/azure-dev/cli/azd/pkg/tools/maven"
"github.com/azure/azure-dev/cli/azd/pkg/tools/npm"
"github.com/azure/azure-dev/cli/azd/pkg/tools/python"
"github.com/azure/azure-dev/cli/azd/pkg/tools/sqlcmd"
"github.com/azure/azure-dev/cli/azd/pkg/tools/swa"
"github.com/azure/azure-dev/cli/azd/pkg/workflow"
"github.com/mattn/go-colorable"
Expand Down Expand Up @@ -602,6 +603,7 @@ func registerCommonDependencies(container *ioc.NestedContainer) {
container.MustRegisterSingleton(dotnet.NewDotNetCli)
container.MustRegisterSingleton(git.NewGitCli)
container.MustRegisterSingleton(github.NewGitHubCli)
container.MustRegisterSingleton(sqlcmd.NewSqlCmdCli)
container.MustRegisterSingleton(javac.NewCli)
container.MustRegisterSingleton(kubectl.NewKubectl)
container.MustRegisterSingleton(maven.NewMavenCli)
Expand Down
3 changes: 3 additions & 0 deletions cli/azd/internal/tracing/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ const BicepInstallEvent = "tools.bicep.install"
// GitHubCliInstallEvent is the name of the event which tracks the overall GitHub cli install operation.
const GitHubCliInstallEvent = "tools.gh.install"

// SqlCmdCliInstallEvent is the name of the event which tracks the overall sqlcmd cli install operation.
const SqlCmdCliInstallEvent = "tools.sqlCmd.install"

// PackCliInstallEvent is the name of the event which tracks the overall pack cli install operation.
const PackCliInstallEvent = "tools.pack.install"

Expand Down
29 changes: 29 additions & 0 deletions cli/azd/pkg/auth/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,35 @@ func (m *Manager) GetLoggedInServicePrincipalTenantID(ctx context.Context) (*str
return currentUser.TenantID, nil
}

func (m *Manager) GetLoggedInServicePrincipalID(ctx context.Context) (*string, error) {
if m.UseExternalAuth() {
// When delegating to an external system, we have no way to determine what principal was used
return nil, nil
}

cfg, err := m.userConfigManager.Load()
if err != nil {
return nil, fmt.Errorf("fetching current user: %w", err)
}

if shouldUseLegacyAuth(cfg) {
// When delegating to az, we have no way to determine what principal was used
return nil, nil
}

authCfg, err := m.readAuthConfig()
if err != nil {
return nil, fmt.Errorf("fetching auth config: %w", err)
}

currentUser, err := readUserProperties(authCfg)
if err != nil {
return nil, ErrNoCurrentUser
}

return currentUser.ClientID, nil
}

func (m *Manager) newCredentialFromManagedIdentity(clientID string) (azcore.TokenCredential, error) {
options := &azidentity.ManagedIdentityCredentialOptions{}
if clientID != "" {
Expand Down
3 changes: 3 additions & 0 deletions cli/azd/pkg/azure/arm_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ type AzdMetadataType string

const AzdMetadataTypeLocation AzdMetadataType = "location"
const AzdMetadataTypeGenerate AzdMetadataType = "generate"
const AzdMetadataTypePrincipalLogin AzdMetadataType = "principalLogin"
const AzdMetadataTypePrincipalId AzdMetadataType = "principalId"
const AzdMetadataTypePrincipalType AzdMetadataType = "principalType"
const AzdMetadataTypeGenerateOrManual AzdMetadataType = "generateOrManual"

type AzdMetadata struct {
Expand Down
47 changes: 47 additions & 0 deletions cli/azd/pkg/azureutil/principal.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package azureutil
import (
"context"
"fmt"
"log"

"github.com/azure/azure-dev/cli/azd/pkg/auth"
"github.com/azure/azure-dev/cli/azd/pkg/tools/azcli"
Expand Down Expand Up @@ -34,3 +35,49 @@ func GetCurrentPrincipalId(ctx context.Context, userProfile *azcli.UserProfileSe

return oid, nil
}

type LoggedInPrincipalProfileData struct {
PrincipalId string
PrincipalType string
PrincipalLoginName string
}

// LoggedInPrincipalProfile returns the info about the current logged in principal
func LoggedInPrincipalProfile(
vhvb1989 marked this conversation as resolved.
Show resolved Hide resolved
ctx context.Context, userProfile *azcli.UserProfileService, tenantId string) (*LoggedInPrincipalProfileData, error) {
principalProfile, err := userProfile.SignedProfile(ctx, tenantId)
if err == nil {
return &LoggedInPrincipalProfileData{
PrincipalId: principalProfile.Id,
PrincipalType: "User",
PrincipalLoginName: principalProfile.UserPrincipalName,
}, nil
}

token, err := userProfile.GetAccessToken(ctx, tenantId)
if err != nil {
return nil, fmt.Errorf("getting access token: %w", err)
}

tokenClaims, err := auth.GetClaimsFromAccessToken(token.AccessToken)
if err != nil {
return nil, fmt.Errorf("getting oid from token: %w", err)
}

appProfile, err := userProfile.AppProfile(ctx, tenantId)
if err == nil {
return &LoggedInPrincipalProfileData{
PrincipalId: *appProfile.AppId,
PrincipalType: "Application",
PrincipalLoginName: appProfile.DisplayName,
}, nil
} else {
log.Println(fmt.Errorf("fetching current user information: %w", err))
}

return &LoggedInPrincipalProfileData{
PrincipalId: tokenClaims.LocalAccountId(),
PrincipalType: "User",
PrincipalLoginName: tokenClaims.Email,
}, nil
}
60 changes: 47 additions & 13 deletions cli/azd/pkg/infra/provisioning/bicep/bicep_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1967,6 +1967,10 @@ func (p *BicepProvider) ensureParameters(
key string
param azure.ArmTemplateParameterDefinition
}
currentPrincipalProfile, err := p.curPrincipal.CurrentPrincipalProfile(ctx)
if err != nil {
return nil, fmt.Errorf("fetching current principal profile: %w", err)
}

for _, key := range sortedKeys {
param := template.Parameters[key]
Expand Down Expand Up @@ -2009,20 +2013,50 @@ func (p *BicepProvider) ensureParameters(
// If the parameter is tagged with {type: "generate"}, skip prompting.
// We generate it once, then save to config for next attempts.`.
azdMetadata, hasMetadata := param.AzdMetadata()
if hasMetadata && parameterType == ParameterTypeString && azdMetadata.Type != nil &&
*azdMetadata.Type == azure.AzdMetadataTypeGenerate {

// - generate once
genValue, err := autoGenerate(key, azdMetadata)
if err != nil {
return nil, err
}
configuredParameters[key] = azure.ArmParameterValue{
Value: genValue,
if hasMetadata && parameterType == ParameterTypeString && azdMetadata.Type != nil {
vhvb1989 marked this conversation as resolved.
Show resolved Hide resolved
azdMetadataType := *azdMetadata.Type
switch azdMetadataType {
case azure.AzdMetadataTypeGenerate:
// - generate once
genValue, err := autoGenerate(key, azdMetadata)
if err != nil {
return nil, err
}
configuredParameters[key] = azure.ArmParameterValue{
Value: genValue,
}
mustSetParamAsConfig(key, genValue, p.env.Config, param.Secure())
configModified = true
continue
// Check metadata for auto-inject values [principalId, principalType, principalLogin]
case azure.AzdMetadataTypePrincipalLogin:
pLogin := currentPrincipalProfile.PrincipalLoginName
configuredParameters[key] = azure.ArmParameterValue{
Value: pLogin,
}
mustSetParamAsConfig(key, pLogin, p.env.Config, param.Secure())
configModified = true
continue
case azure.AzdMetadataTypePrincipalId:
pLogin := currentPrincipalProfile.PrincipalId
configuredParameters[key] = azure.ArmParameterValue{
Value: pLogin,
}
mustSetParamAsConfig(key, pLogin, p.env.Config, param.Secure())
configModified = true
continue
case azure.AzdMetadataTypePrincipalType:
pLogin := currentPrincipalProfile.PrincipalType
configuredParameters[key] = azure.ArmParameterValue{
Value: pLogin,
}
mustSetParamAsConfig(key, pLogin, p.env.Config, param.Secure())
configModified = true
continue
default:
// Do nothing
log.Println("Skipping actions for azd unknown metadata bicep parameter with type: ", azdMetadataType)
}
mustSetParamAsConfig(key, genValue, p.env.Config, param.Secure())
configModified = true
continue
}

// No saved value for this required parameter, we'll need to prompt for it.
Expand Down
7 changes: 7 additions & 0 deletions cli/azd/pkg/infra/provisioning/bicep/prompt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/azure/azure-dev/cli/azd/pkg/account"
"github.com/azure/azure-dev/cli/azd/pkg/azure"
"github.com/azure/azure-dev/cli/azd/pkg/azureutil"
"github.com/azure/azure-dev/cli/azd/pkg/cloud"
"github.com/azure/azure-dev/cli/azd/pkg/convert"
"github.com/azure/azure-dev/cli/azd/pkg/environment"
Expand Down Expand Up @@ -320,6 +321,12 @@ func TestPromptForParametersLocation(t *testing.T) {

type mockCurrentPrincipal struct{}

// CurrentPrincipalProfile implements provisioning.CurrentPrincipalIdProvider.
func (m *mockCurrentPrincipal) CurrentPrincipalProfile(
ctx context.Context) (*azureutil.LoggedInPrincipalProfileData, error) {
return &azureutil.LoggedInPrincipalProfileData{}, nil
}

func (m *mockCurrentPrincipal) CurrentPrincipalId(_ context.Context) (string, error) {
return "11111111-1111-1111-1111-111111111111", nil
}
15 changes: 15 additions & 0 deletions cli/azd/pkg/infra/provisioning/current_principal_id_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type CurrentPrincipalIdProvider interface {
// CurrentPrincipalId returns the object id of the current logged in principal, or an error if it can not be
// determined.
CurrentPrincipalId(ctx context.Context) (string, error)
CurrentPrincipalProfile(ctx context.Context) (*azureutil.LoggedInPrincipalProfileData, error)
}

func NewPrincipalIdProvider(
Expand Down Expand Up @@ -47,3 +48,17 @@ func (p *principalIDProvider) CurrentPrincipalId(ctx context.Context) (string, e

return principalId, nil
}

func (p *principalIDProvider) CurrentPrincipalProfile(ctx context.Context) (*azureutil.LoggedInPrincipalProfileData, error) {
tenantId, err := p.subResolver.LookupTenant(ctx, p.env.GetSubscriptionId())
if err != nil {
return nil, fmt.Errorf("getting tenant id for subscription %s. Error: %w", p.env.GetSubscriptionId(), err)
}

principalProfile, err := azureutil.LoggedInPrincipalProfile(ctx, p.userProfileService, tenantId)
if err != nil {
return nil, fmt.Errorf("fetching current user information: %w", err)
}

return principalProfile, nil
}
Loading
Loading