Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
371c137
remove setting from sample projects and create a note about it for ba…
vhvb1989 Apr 29, 2025
2a6a6fb
remove the need of need for deployment param. This is not required af…
vhvb1989 Apr 29, 2025
56e5296
allow using prefixed env vars during azd deploy for Aspire (dotnet + …
vhvb1989 Apr 29, 2025
d108984
Link parameters with default value to outputs in bicep generation
vhvb1989 Apr 29, 2025
cd82e62
fix generating the name for env var mapping
vhvb1989 Apr 29, 2025
f2371d8
ParameterWithDefault can use azd env as state
vhvb1989 Apr 30, 2025
927b734
wip
vhvb1989 Apr 30, 2025
2e6d097
ready to test
vhvb1989 May 1, 2025
05e5ab9
Configure Azure Developer Pipeline
vhvb1989 May 1, 2025
da82601
fix error condition
vhvb1989 May 1, 2025
08fc120
fix unmarshall value
vhvb1989 May 1, 2025
d3e614e
remove solution
vhvb1989 May 1, 2025
d8c6220
move workflow auto creation to manager configure
vhvb1989 May 1, 2025
d86d688
updating tests... WIP
vhvb1989 May 1, 2025
bf75f09
ready to test
vhvb1989 May 1, 2025
7fdbeb1
lint
vhvb1989 May 1, 2025
2d1ad2d
aspire snap
vhvb1989 May 1, 2025
be26c32
update snap
vhvb1989 May 1, 2025
9dc6cbd
remove param bc vsserver test can't handle prompt
vhvb1989 May 1, 2025
47490d4
Merge branch 'main' of github.com:Azure/azure-dev into get-rid-of-ini…
vhvb1989 May 9, 2025
6ea6527
Merge branch 'main' of github.com:Azure/azure-dev into get-rid-of-ini…
vhvb1989 May 9, 2025
9b7bc43
apply PR suggestions
vhvb1989 May 9, 2025
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
9 changes: 8 additions & 1 deletion cli/azd/cmd/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,14 @@ func (p *pipelineConfigAction) Run(ctx context.Context) (*actions.ActionResult,
Title: fmt.Sprintf("Configure your %s pipeline", pipelineProviderName),
})

pipelineResult, err := p.manager.Configure(ctx, p.projectConfig.Name)
// Pull provider specific parameters
providerParameters, err := p.provisioningManager.Parameters(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get parameters for provider %s: %w", pipelineProviderName, err)
}
p.manager.SetParameters(providerParameters)

pipelineResult, err := p.manager.Configure(ctx, p.projectConfig.Name, infra)
if err != nil {
return nil, err
}
Expand Down
8 changes: 6 additions & 2 deletions cli/azd/internal/scaffold/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,12 @@ var camelCaseRegex = regexp.MustCompile(`([a-z0-9])([A-Z])`)
// EnvFormat takes an input parameter like `fooParam` which is expected to be in camel case and returns it in
// upper snake case with env var template, like `${AZURE_FOO_PARAM}`.
func EnvFormat(src string) string {
snake := strings.ReplaceAll(strings.ToUpper(camelCaseRegex.ReplaceAllString(src, "${1}_${2}")), "-", "_")
return fmt.Sprintf("${AZURE_%s}", snake)
return fmt.Sprintf("${%s}", AzureSnakeCase(src))
}

func AzureSnakeCase(src string) string {
return fmt.Sprintf(
"AZURE_%s", strings.ReplaceAll(strings.ToUpper(camelCaseRegex.ReplaceAllString(src, "${1}_${2}")), "-", "_"))
}

func HasACA(services []ServiceSpec) bool {
Expand Down
7 changes: 3 additions & 4 deletions cli/azd/pkg/apphost/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,9 @@ func init() {
"bicepParameterName": func(src string) string {
return strings.ReplaceAll(src, "-", "_")
},
"removeDot": scaffold.RemoveDotAndDash,
"envFormat": scaffold.EnvFormat,
"removeDot": scaffold.RemoveDotAndDash,
"envFormat": scaffold.EnvFormat,
"azureSnakeCase": scaffold.AzureSnakeCase,
"bicepParameterValue": func(value *string) string {
if value == nil {
return ""
Expand Down Expand Up @@ -337,8 +338,6 @@ func BicepTemplate(name string, manifest *Manifest, options AppHostOptions) (*me
// if not nil, like empty string or any other string, it is used as `= '<value>'`
if parameter.Default.Value != nil {
parameterDefaultValue = parameter.Default.Value
metadataType = azure.AzdMetadataTypeNeedForDeploy
parameterMetadata = "{}"
} else if parameter.Default.Generate != nil { // Note: .Value and .Generate are mutually exclusive
pMetadata, err := inputMetadata(*parameter.Default.Generate)
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,7 @@ param noVolume_pas_sw_ord string
})
@secure()
param noVolume_password string
@metadata({azd: {
type: 'needForDeploy'
config: {}
}
})
param param_with_empty_value string = ''
@metadata({azd: {
type: 'needForDeploy'
config: {}
}
})
param param_with_value string = 'default value for param'

var tags = {
Expand Down Expand Up @@ -100,4 +90,6 @@ output SERVICE_NOVOLUME_FILE_SHARE_BM0_NAME string = resources.outputs.SERVICE_N
output SERVICE_NOVOLUME_VOLUME_BM1_NAME string = resources.outputs.SERVICE_NOVOLUME_VOLUME_BM1_NAME
output SERVICE_NOVOLUME_FILE_SHARE_BM1_NAME string = resources.outputs.SERVICE_NOVOLUME_FILE_SHARE_BM1_NAME
output AZURE_VOLUMES_STORAGE_ACCOUNT string = resources.outputs.AZURE_VOLUMES_STORAGE_ACCOUNT
output AZURE_PARAM_WITH_EMPTY_VALUE string = param-with-empty-value
output AZURE_PARAM_WITH_VALUE string = param-with-value

1 change: 0 additions & 1 deletion cli/azd/pkg/azure/arm_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ type AzdMetadataType string
const AzdMetadataTypeLocation AzdMetadataType = "location"
const AzdMetadataTypeGenerate AzdMetadataType = "generate"
const AzdMetadataTypeGenerateOrManual AzdMetadataType = "generateOrManual"
const AzdMetadataTypeNeedForDeploy AzdMetadataType = "needForDeploy"
const AzdMetadataTypeResourceGroup AzdMetadataType = "resourceGroup"

type AzdMetadata struct {
Expand Down
5 changes: 5 additions & 0 deletions cli/azd/pkg/devcenter/provision_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,3 +531,8 @@ func hasInfraTemplates(path string) bool {

return len(entries) > 0
}

func (p *ProvisionProvider) Parameters(ctx context.Context) ([]provisioning.Parameter, error) {
// not supported (no-op)
return nil, nil
}
9 changes: 9 additions & 0 deletions cli/azd/pkg/environment/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ type Environment struct {
Config config.Config
}

// AzdInitialEnvironmentConfigName is part of a strategy to re-construct AZD environment in CI/CD from an initial state.
// This strategy was introduced for templates which takes input parameters. Parameters are saved to azd's environment
// configuration (.azure/env-name/config.json). This file is not committed to source control, so the saved values can't
// be used during CI/CD. AZD uses AZD_INITIAL_ENVIRONMENT_CONFIG to smuggle all saved parameters into a CI/CD secret and
// use it to create the environment configuration file (the first time AZD runs and creates a new environment).
//
// While AZD_INITIAL_ENVIRONMENT_CONFIG is still supported for backwards compatibility, it is deprecated.
// The currently strategy is to create individual variables or secrets for the CI/CD pipeline depending on the parameter
// configuration.
const AzdInitialEnvironmentConfigName = "AZD_INITIAL_ENVIRONMENT_CONFIG"

// New returns a new environment with the specified name.
Expand Down
75 changes: 59 additions & 16 deletions cli/azd/pkg/infra/provisioning/bicep/bicep_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1524,6 +1524,13 @@ func (p *BicepProvider) createOutputParameters(
type loadParametersResult struct {
parameters map[string]azure.ArmParameter
locationParams []string
// envMapping is a map of parameter name to environment variable names
// holds information about which parameters are mapped to which env vars for
// cases like "param": "${env:AZURE_FOO}-${env:AZURE_BAR}", envMapping will
// contain {"param": ["AZURE_FOO", "AZURE_BAR"]}
// This information is useful for setting a CI/CD automatically. Each env var
// will be set to the value of the parameter as variable or secret.
envMapping map[string][]string
}

// loadParameters reads the parameters file template for environment/module specified by Options,
Expand Down Expand Up @@ -1561,13 +1568,15 @@ func (p *BicepProvider) loadParameters(ctx context.Context) (loadParametersResul

parametersMappedToAzureLocation := []string{}
resolvedParams := map[string]azure.ArmParameter{}
envMapping := map[string][]string{}

// resolving each parameter to keep track of the name during the resolution.
// We used to resolve all the file before, supporting env var substitution at any part of the file.
// We want to support substitution only for the parameter value.
// We also need to identify which parameters are mapped to AZURE_LOCATION (if any).
// We also want to exclude parameters mapped to env vars which env var is not set (instead of using empty string).
for paramName, param := range decodedParamsFile.Parameters {
mappedEnvVars := []string{}
paramBytes, err := json.Marshal(param)
if err != nil {
return loadParametersResult{}, fmt.Errorf("error decoding deployment parameter %s: %w", paramName, err)
Expand All @@ -1581,6 +1590,9 @@ func (p *BicepProvider) loadParameters(ctx context.Context) (loadParametersResul
if name == environment.LocationEnvVarName {
parametersMappedToAzureLocation = append(parametersMappedToAzureLocation, paramName)
}
// principalId and locations are intentionally excluded from the mapped env vars as
// they are global env vars
mappedEnvVars = append(mappedEnvVars, name)
if _, isDefined := p.env.LookupEnv(name); !isDefined {
hasUnsetEnvVar = true
}
Expand All @@ -1589,6 +1601,7 @@ func (p *BicepProvider) loadParameters(ctx context.Context) (loadParametersResul
if err != nil {
return loadParametersResult{}, fmt.Errorf("substituting environment variables for %s: %w", paramName, err)
}
envMapping[paramName] = mappedEnvVars
// resolve `secretOrRandomPassword` -> this is a way to ask AZD to generate a password for the user and
// store it in a Key Vault. But if the Key Vault and secret exists, AZD just takes the secret from there.
if cmdsubst.ContainsCommandInvocation(replaced, cmdsubst.SecretOrRandomPasswordCommandName) {
Expand Down Expand Up @@ -1634,6 +1647,7 @@ func (p *BicepProvider) loadParameters(ctx context.Context) (loadParametersResul
return loadParametersResult{
parameters: resolvedParams,
locationParams: parametersMappedToAzureLocation,
envMapping: envMapping,
}, nil
}

Expand Down Expand Up @@ -2003,7 +2017,6 @@ func (p *BicepProvider) ensureParameters(
azdMetadata, hasMetadata := param.AzdMetadata()

// If a value is explicitly configured via a parameters file, use it.
// unless the parameter value inference is nil/empty
if v, has := parameters[key]; has {
// Directly pass through Key Vault references without prompting.
if v.KeyVaultReference != nil {
Expand All @@ -2027,24 +2040,9 @@ func (p *BicepProvider) ensureParameters(
}
}

needForDeployParameter := hasMetadata &&
azdMetadata.Type != nil &&
*azdMetadata.Type == azure.AzdMetadataTypeNeedForDeploy
if needForDeployParameter && paramValue == "" && param.DefaultValue != nil {
// Parameters with needForDeploy metadata don't support overriding with empty values when a default
// value is present. If the value is empty, we'll use the default value instead.
defValue, castOk := param.DefaultValue.(string)
if castOk {
paramValue = defValue
}
}
configuredParameters[key] = azure.ArmParameter{
Value: paramValue,
}
if needForDeployParameter {
mustSetParamAsConfig(key, paramValue, p.env.Config, param.Secure())
configModified = true
}
continue
}
}
Expand Down Expand Up @@ -2293,3 +2291,48 @@ func NewBicepProvider(
azureClient: azureClient,
}
}

func (p *BicepProvider) Parameters(ctx context.Context) ([]provisioning.Parameter, error) {
modulePath := p.modulePath()
compileResult, err := p.compileBicep(ctx, modulePath)
if err != nil {
return nil, fmt.Errorf("creating template: %w", err)
}
// templateParameters are the parameters defined in the bicep template. We know when a parameter is secured,
// its type and its default value from this definition.
templateParameters := compileResult.Template.Parameters

// parametersInfo contains the env vars mappings (from a parameters file). bicepparam is not supported yet.
parametersInfo, err := p.loadParameters(ctx)
if err != nil {
return nil, fmt.Errorf("loading parameters: %w", err)
}

// resolved parameters contains the final value for the parameters after evaluating. The final value can be
// from env var, from default value or from user input (prompt).
resolvedParams, err := p.ensureParameters(ctx, compileResult.Template)
if err != nil {
return nil, fmt.Errorf("resolving parameters: %w", err)
}

provisionParameters := make([]provisioning.Parameter, 0, len(templateParameters))
for key, param := range templateParameters {
if _, usingParam := resolvedParams[key]; !usingParam {
// No resolved param for this parameter definition.
continue
}
_, isPrompt := p.env.Config.Get(fmt.Sprintf("infra.parameters.%s", key))
provisionParameters = append(provisionParameters, provisioning.Parameter{
Name: key,
Secret: param.Secure(),
Value: resolvedParams[key].Value,
EnvVarMapping: parametersInfo.envMapping[key],
// No env var mapping and param is persisted in env config infra.parameters means local prompt only
// If user set an env var mapping after a local prompt, the env var overrides the value persisted in config
// which turns local prompt false
LocalPrompt: isPrompt && len(parametersInfo.envMapping[key]) == 0,
})
}

return provisionParameters, nil
}
8 changes: 8 additions & 0 deletions cli/azd/pkg/infra/provisioning/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ func (m *Manager) Initialize(ctx context.Context, projectPath string, options Op
return m.provider.Initialize(ctx, projectPath, options)
}

// Parameters gets the list of parameters and its value which will be used to provision the infrastructure.
func (m *Manager) Parameters(ctx context.Context) ([]Parameter, error) {
if m.provider == nil {
panic("called parameters() with provider not initialized. Make sure to call manager.Initialize() first.")
}
return m.provider.Parameters(ctx)
}

// Gets the latest deployment details for the specified scope
func (m *Manager) State(ctx context.Context, options *StateOptions) (*StateResult, error) {
result, err := m.provider.State(ctx, options)
Expand Down
10 changes: 10 additions & 0 deletions cli/azd/pkg/infra/provisioning/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ type StateResult struct {
State *State
}

type Parameter struct {
Name string
Secret bool
Value any
EnvVarMapping []string
// true when the parameter value was set by the user from the command line (prompt)
LocalPrompt bool
}

type Provider interface {
Name() string
Initialize(ctx context.Context, projectPath string, options Options) error
Expand All @@ -59,4 +68,5 @@ type Provider interface {
Preview(ctx context.Context) (*DeployPreviewResult, error)
Destroy(ctx context.Context, options DestroyOptions) (*DestroyResult, error)
EnsureEnv(ctx context.Context) error
Parameters(ctx context.Context) ([]Parameter, error)
}
Original file line number Diff line number Diff line change
Expand Up @@ -765,3 +765,8 @@ type terraformChildModule struct {
Resources []terraformResource `json:"resources"`
ChildModules []terraformChildModule `json:"child_modules"`
}

func (t *TerraformProvider) Parameters(ctx context.Context) ([]provisioning.Parameter, error) {
// not supported (no-op)
return nil, nil
}
5 changes: 5 additions & 0 deletions cli/azd/pkg/infra/provisioning/test/test_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ func (p *TestProvider) Destroy(
return &destroyResult, nil
}

func (p *TestProvider) Parameters(ctx context.Context) ([]provisioning.Parameter, error) {
// not supported (no-op)
return nil, nil
}

func NewTestProvider(
envManager environment.Manager,
env *environment.Environment,
Expand Down
Loading
Loading