in cli/azd/internal/cmd/provision.go [160:398]
func (p *ProvisionAction) Run(ctx context.Context) (*actions.ActionResult, error) {
if p.flags.noProgress {
fmt.Fprintln(
p.console.Handles().Stderr,
//nolint:Lll
output.WithWarningFormat(
"WARNING: The '--no-progress' flag is deprecated and will be removed in a future release.",
),
)
}
previewMode := p.flags.preview
// Command title
defaultTitle := "Provisioning Azure resources (azd provision)"
defaultTitleNote := "Provisioning Azure resources can take some time"
if previewMode {
defaultTitle = "Previewing Azure resource changes (azd provision --preview)"
defaultTitleNote = "This is a preview. No changes will be applied to your Azure resources."
}
p.console.MessageUxItem(ctx, &ux.MessageTitle{
Title: defaultTitle,
TitleNote: defaultTitleNote},
)
startTime := time.Now()
if err := p.projectManager.Initialize(ctx, p.projectConfig); err != nil {
return nil, err
}
if err := p.projectManager.EnsureAllTools(ctx, p.projectConfig, nil); err != nil {
return nil, err
}
infra, err := p.importManager.ProjectInfrastructure(ctx, p.projectConfig)
if err != nil {
return nil, err
}
defer func() { _ = infra.Cleanup() }()
infraOptions := infra.Options
infraOptions.IgnoreDeploymentState = p.flags.ignoreDeploymentState
if err := p.provisionManager.Initialize(ctx, p.projectConfig.Path, infraOptions); err != nil {
return nil, fmt.Errorf("initializing provisioning manager: %w", err)
}
// Get Subscription to Display in Command Title Note
// Subscription and Location are ONLY displayed when they are available (found from env), otherwise, this message
// is not displayed.
// This needs to happen after the provisionManager initializes to make sure the env is ready for the provisioning
// provider
subscription, subErr := p.subManager.GetSubscription(ctx, p.env.GetSubscriptionId())
if subErr == nil {
location, err := p.subManager.GetLocation(ctx, p.env.GetSubscriptionId(), p.env.GetLocation())
var locationDisplay string
if err != nil {
log.Printf("failed getting location: %v", err)
} else {
locationDisplay = location.DisplayName
}
var subscriptionDisplay string
if v, err := strconv.ParseBool(os.Getenv("AZD_DEMO_MODE")); err == nil && v {
subscriptionDisplay = subscription.Name
} else {
subscriptionDisplay = fmt.Sprintf("%s (%s)", subscription.Name, subscription.Id)
}
p.console.MessageUxItem(ctx, &ux.EnvironmentDetails{
Subscription: subscriptionDisplay,
Location: locationDisplay,
})
} else {
log.Printf("failed getting subscriptions. Skip displaying sub and location: %v", subErr)
}
var deployResult *provisioning.DeployResult
var deployPreviewResult *provisioning.DeployPreviewResult
projectEventArgs := project.ProjectLifecycleEventArgs{
Project: p.projectConfig,
Args: map[string]any{
"preview": previewMode,
},
}
if p.alphaFeatureManager.IsEnabled(azapi.FeatureDeploymentStacks) {
p.console.WarnForFeature(ctx, azapi.FeatureDeploymentStacks)
}
err = p.projectConfig.Invoke(ctx, project.ProjectEventProvision, projectEventArgs, func() error {
var err error
if previewMode {
deployPreviewResult, err = p.provisionManager.Preview(ctx)
} else {
deployResult, err = p.provisionManager.Deploy(ctx)
}
return err
})
if err != nil {
if p.formatter.Kind() == output.JsonFormat {
stateResult, err := p.provisionManager.State(ctx, nil)
if err != nil {
return nil, fmt.Errorf(
"deployment failed and the deployment result is unavailable: %w",
multierr.Combine(err, err),
)
}
if err := p.formatter.Format(
provisioning.NewEnvRefreshResultFromState(stateResult.State), p.writer, nil); err != nil {
return nil, fmt.Errorf(
"deployment failed and the deployment result could not be displayed: %w",
multierr.Combine(err, err),
)
}
}
//if user don't have access to openai
errorMsg := err.Error()
if strings.Contains(errorMsg, specialFeatureOrQuotaIdRequired) && strings.Contains(errorMsg, "OpenAI") {
requestAccessLink := "https://go.microsoft.com/fwlink/?linkid=2259205&clcid=0x409"
return nil, &internal.ErrorWithSuggestion{
Err: err,
Suggestion: "\nSuggested Action: The selected subscription does not have access to" +
" Azure OpenAI Services. Please visit " + output.WithLinkFormat("%s", requestAccessLink) +
" to request access.",
}
}
if strings.Contains(errorMsg, AINotValid) &&
strings.Contains(errorMsg, openAIsubscriptionNoQuotaId) {
return nil, &internal.ErrorWithSuggestion{
Suggestion: "\nSuggested Action: The selected " +
"subscription has not been enabled for use of Azure AI service and does not have quota for " +
"any pricing tiers. Please visit " + output.WithLinkFormat("%s", p.portalUrlBase) +
" and select 'Create' on specific services to request access.",
Err: err,
}
}
//if user haven't agree to Responsible AI terms
if strings.Contains(errorMsg, responsibleAITerms) {
return nil, &internal.ErrorWithSuggestion{
Suggestion: "\nSuggested Action: Please visit azure portal in " +
output.WithLinkFormat("%s", p.portalUrlBase) + ". Create the resource in azure portal " +
"to go through Responsible AI terms, and then delete it. " +
"After that, run 'azd provision' again",
Err: err,
}
}
return nil, fmt.Errorf("deployment failed: %w", err)
}
if previewMode {
p.console.MessageUxItem(ctx, deployResultToUx(deployPreviewResult))
return &actions.ActionResult{
Message: &actions.ResultMessage{
Header: fmt.Sprintf(
"Generated provisioning preview in %s.", ux.DurationAsText(since(startTime))),
FollowUp: getResourceGroupFollowUp(
ctx,
p.formatter,
p.portalUrlBase,
p.projectConfig,
p.resourceManager,
p.env,
true,
),
},
}, nil
}
if deployResult.SkippedReason == provisioning.DeploymentStateSkipped {
return &actions.ActionResult{
Message: &actions.ResultMessage{
Header: "There are no changes to provision for your application.",
},
}, nil
}
servicesStable, err := p.importManager.ServiceStable(ctx, p.projectConfig)
if err != nil {
return nil, err
}
for _, svc := range servicesStable {
eventArgs := project.ServiceLifecycleEventArgs{
Project: p.projectConfig,
Service: svc,
Args: map[string]any{
"bicepOutput": deployResult.Deployment.Outputs,
},
}
if err := svc.RaiseEvent(ctx, project.ServiceEventEnvUpdated, eventArgs); err != nil {
return nil, err
}
}
if p.formatter.Kind() == output.JsonFormat {
stateResult, err := p.provisionManager.State(ctx, nil)
if err != nil {
return nil, fmt.Errorf(
"deployment succeeded but the deployment result is unavailable: %w",
multierr.Combine(err, err),
)
}
if err := p.formatter.Format(
provisioning.NewEnvRefreshResultFromState(stateResult.State), p.writer, nil); err != nil {
return nil, fmt.Errorf(
"deployment succeeded but the deployment result could not be displayed: %w",
multierr.Combine(err, err),
)
}
}
return &actions.ActionResult{
Message: &actions.ResultMessage{
Header: fmt.Sprintf(
"Your application was provisioned in Azure in %s.", ux.DurationAsText(since(startTime))),
FollowUp: getResourceGroupFollowUp(
ctx,
p.formatter,
p.portalUrlBase,
p.projectConfig,
p.resourceManager,
p.env,
false,
),
},
}, nil
}