Skip to content

Commit

Permalink
WIP: provisioning: Consider resourceGroup from azure.yaml
Browse files Browse the repository at this point in the history
We allow specifying the resource group which is used for discovery of
resources (instead of probing for a resource group taged with
`azd-env-name` inside `azure.yaml`. When doing a resource group scoped
deployment, we should consider this value if set and use it instead of
prompting the user to select a resource group or create a new one.

Fixes Azure#4287
  • Loading branch information
ellismg committed Sep 13, 2024
1 parent ec85b9c commit 781ae4e
Show file tree
Hide file tree
Showing 18 changed files with 83 additions and 28 deletions.
3 changes: 2 additions & 1 deletion cli/azd/cmd/down.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ func (a *downAction) Run(ctx context.Context) (*actions.ActionResult, error) {
}
defer func() { _ = infra.Cleanup() }()

if err := a.provisionManager.Initialize(ctx, a.projectConfig.Path, infra.Options); err != nil {
err = a.provisionManager.Initialize(ctx, a.projectConfig.Path, infra.Options, a.projectConfig.ResourceGroupName)
if err != nil {
return nil, fmt.Errorf("initializing provisioning manager: %w", err)
}

Expand Down
2 changes: 1 addition & 1 deletion cli/azd/cmd/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ func (ef *envRefreshAction) Run(ctx context.Context) (*actions.ActionResult, err
defer func() { _ = infra.Cleanup() }()

// env refresh supports "BYOI" infrastructure where bicep isn't available
err = ef.provisionManager.Initialize(ctx, ef.projectConfig.Path, infra.Options)
err = ef.provisionManager.Initialize(ctx, ef.projectConfig.Path, infra.Options, ef.projectConfig.ResourceGroupName)
if errors.Is(err, bicep.ErrEnsureEnvPreReqBicepCompileFailed) {
// If bicep is not available, we continue to prompt for subscription and location unfiltered
err = provisioning.EnsureSubscriptionAndLocation(ctx, ef.envManager, ef.env, ef.prompters,
Expand Down
2 changes: 1 addition & 1 deletion cli/azd/cmd/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func (p *pipelineConfigAction) Run(ctx context.Context) (*actions.ActionResult,
}
defer func() { _ = infra.Cleanup() }()

err = p.provisioningManager.Initialize(ctx, p.projectConfig.Path, infra.Options)
err = p.provisioningManager.Initialize(ctx, p.projectConfig.Path, infra.Options, p.projectConfig.ResourceGroupName)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion cli/azd/cmd/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func (u *upAction) Run(ctx context.Context) (*actions.ActionResult, error) {

// TODO(weilim): remove this once we have decided if it's okay to not set AZURE_SUBSCRIPTION_ID and AZURE_LOCATION
// early in the up workflow in #3745
err = u.provisioningManager.Initialize(ctx, u.projectConfig.Path, infra.Options)
err = u.provisioningManager.Initialize(ctx, u.projectConfig.Path, infra.Options, u.projectConfig.ResourceGroupName)
if errors.Is(err, bicep.ErrEnsureEnvPreReqBicepCompileFailed) {
// If bicep is not available, we continue to prompt for subscription and location unfiltered
err = provisioning.EnsureSubscriptionAndLocation(ctx, u.envManager, u.env, u.prompters, nil)
Expand Down
3 changes: 2 additions & 1 deletion cli/azd/internal/cmd/provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,8 @@ func (p *ProvisionAction) Run(ctx context.Context) (*actions.ActionResult, error

infraOptions := infra.Options
infraOptions.IgnoreDeploymentState = p.flags.ignoreDeploymentState
if err := p.provisionManager.Initialize(ctx, p.projectConfig.Path, infraOptions); err != nil {
err = p.provisionManager.Initialize(ctx, p.projectConfig.Path, infraOptions, p.projectConfig.ResourceGroupName)
if err != nil {
return nil, fmt.Errorf("initializing provisioning manager: %w", err)
}

Expand Down
4 changes: 3 additions & 1 deletion cli/azd/internal/vsrpc/environment_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,9 @@ func (s *environmentService) DeleteEnvironmentAsync(
}
defer func() { _ = projectInfra.Cleanup() }()

if err := c.provisionManager.Initialize(ctx, c.projectConfig.Path, projectInfra.Options); err != nil {
err = c.provisionManager.Initialize(
ctx, c.projectConfig.Path, projectInfra.Options, c.projectConfig.ResourceGroupName)
if err != nil {
return false, fmt.Errorf("initializing provisioning manager: %w", err)
}

Expand Down
3 changes: 2 additions & 1 deletion cli/azd/internal/vsrpc/environment_service_refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/azure/azure-dev/cli/azd/pkg/infra"
"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning"
"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning/bicep"
"github.com/azure/azure-dev/cli/azd/pkg/osutil"
"github.com/azure/azure-dev/cli/azd/pkg/project"
)

Expand Down Expand Up @@ -82,7 +83,7 @@ func (s *environmentService) refreshEnvironmentAsync(
}
defer func() { _ = infra.Cleanup() }()

if err := bicepProvider.Initialize(ctx, c.projectConfig.Path, infra.Options); err != nil {
if err := bicepProvider.Initialize(ctx, c.projectConfig.Path, infra.Options, osutil.EmptyExpandableString); err != nil {
return nil, fmt.Errorf("initializing provisioning manager: %w", err)
}

Expand Down
8 changes: 7 additions & 1 deletion cli/azd/pkg/devcenter/provision_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/azure/azure-dev/cli/azd/pkg/infra"
"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning"
"github.com/azure/azure-dev/cli/azd/pkg/input"
"github.com/azure/azure-dev/cli/azd/pkg/osutil"
"github.com/azure/azure-dev/cli/azd/pkg/output"
)

Expand Down Expand Up @@ -74,7 +75,12 @@ func (p *ProvisionProvider) Name() string {
}

// Initialize initializes the provider
func (p *ProvisionProvider) Initialize(ctx context.Context, projectPath string, options provisioning.Options) error {
func (p *ProvisionProvider) Initialize(
ctx context.Context,
projectPath string,
options provisioning.Options,
_ osutil.ExpandableString,
) error {
p.options = options

return p.EnsureEnv(ctx)
Expand Down
7 changes: 4 additions & 3 deletions cli/azd/pkg/devcenter/provision_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/azure/azure-dev/cli/azd/pkg/infra"
"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning"
"github.com/azure/azure-dev/cli/azd/pkg/input"
"github.com/azure/azure-dev/cli/azd/pkg/osutil"
"github.com/azure/azure-dev/cli/azd/test/mocks"
"github.com/azure/azure-dev/cli/azd/test/mocks/mockdevcentersdk"
"github.com/azure/azure-dev/cli/azd/test/mocks/mockenv"
Expand All @@ -40,7 +41,7 @@ func Test_ProvisionProvider_Initialize(t *testing.T) {
_ = env.Config.Set("platform.config", configMap)

provider := newProvisionProviderForTest(t, mockContext, config, env, nil)
err = provider.Initialize(*mockContext.Context, "project/path", provisioning.Options{})
err = provider.Initialize(*mockContext.Context, "project/path", provisioning.Options{}, osutil.EmptyExpandableString)
require.NoError(t, err)
})

Expand Down Expand Up @@ -68,7 +69,7 @@ func Test_ProvisionProvider_Initialize(t *testing.T) {
}).Respond(selectedEnvironmentTypeIndex)

provider := newProvisionProviderForTest(t, mockContext, config, env, nil)
err = provider.Initialize(*mockContext.Context, "project/path", provisioning.Options{})
err = provider.Initialize(*mockContext.Context, "project/path", provisioning.Options{}, osutil.EmptyExpandableString)
require.NoError(t, err)

actualEnvironmentType, ok := env.Config.Get(DevCenterEnvTypePath)
Expand Down Expand Up @@ -199,7 +200,7 @@ func Test_ProvisionProvider_Deploy(t *testing.T) {

provider := newProvisionProviderForTest(t, mockContext, config, env, manager)

err := provider.Initialize(*mockContext.Context, "project/path", provisioning.Options{})
err := provider.Initialize(*mockContext.Context, "project/path", provisioning.Options{}, osutil.EmptyExpandableString)
require.NoError(t, err)

result, err := provider.Deploy(*mockContext.Context)
Expand Down
31 changes: 28 additions & 3 deletions cli/azd/pkg/infra/provisioning/bicep/bicep_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning"
"github.com/azure/azure-dev/cli/azd/pkg/input"
"github.com/azure/azure-dev/cli/azd/pkg/keyvault"
"github.com/azure/azure-dev/cli/azd/pkg/osutil"
"github.com/azure/azure-dev/cli/azd/pkg/output"
"github.com/azure/azure-dev/cli/azd/pkg/output/ux"
"github.com/azure/azure-dev/cli/azd/pkg/password"
Expand Down Expand Up @@ -91,7 +92,12 @@ func (p *BicepProvider) RequiredExternalTools() []tools.ExternalTool {

// Initialize initializes provider state from the options.
// It also calls EnsureEnv, which ensures the client-side state is ready for provisioning.
func (p *BicepProvider) Initialize(ctx context.Context, projectPath string, options provisioning.Options) error {
func (p *BicepProvider) Initialize(
ctx context.Context,
projectPath string,
options provisioning.Options,
rg osutil.ExpandableString,
) error {
p.projectPath = projectPath
p.options = options
if p.options.Module == "" {
Expand All @@ -108,7 +114,7 @@ func (p *BicepProvider) Initialize(ctx context.Context, projectPath string, opti
p.ignoreDeploymentState = options.IgnoreDeploymentState

p.console.ShowSpinner(ctx, "Initialize bicep provider", input.Step)
err := p.EnsureEnv(ctx)
err := p.EnsureEnv(ctx, rg)
p.console.StopSpinner(ctx, "", input.Step)
return err
}
Expand All @@ -117,7 +123,7 @@ var ErrEnsureEnvPreReqBicepCompileFailed = errors.New("")

// EnsureEnv ensures that the environment is in a provision-ready state with required values set, prompting the user if
// values are unset. This also requires that the Bicep module can be compiled.
func (p *BicepProvider) EnsureEnv(ctx context.Context) error {
func (p *BicepProvider) EnsureEnv(ctx context.Context, rg osutil.ExpandableString) error {
modulePath := p.modulePath()

// for .bicepparam, we first prompt for environment values before calling compiling bicepparam file
Expand Down Expand Up @@ -163,6 +169,25 @@ func (p *BicepProvider) EnsureEnv(ctx context.Context) error {
}

if scope == azure.DeploymentScopeResourceGroup {
rgEval, err := rg.Envsubst(p.env.Getenv)
if err != nil {
return err
}

if rgEval != "" {
// TODO(ellismg): We set this here because the rest of the system reads from `.env` when it needs
// AZURE_RESOURCE_GROUP. But I'm not sure this is exactly what we want to be doing here - it feels wrong
// on some level that changing the value in `azure.yaml` ends up replacing what was in the `.env` file. It seems
// like if it is explicitly set in `azure.yaml` than we do not need to store it in the `.env` file at all.
//
// We also need to understand the policy of what happens if both are set - which one wins? I think that perhaps
// it should be `AZURE_RESOURCE_GROUP` since that also allows overriding via `AZURE_RESOURCE_GROUP=foo azd up`.
p.env.DotenvSet(environment.ResourceGroupEnvVarName, rgEval)
if err := p.envManager.Save(ctx, p.env); err != nil {
return fmt.Errorf("saving resource group name: %w", err)
}
}

if p.env.Getenv(environment.ResourceGroupEnvVarName) == "" {
rgName, err := p.prompters.PromptResourceGroup(ctx)
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion cli/azd/pkg/infra/provisioning/bicep/bicep_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning"
"github.com/azure/azure-dev/cli/azd/pkg/input"
"github.com/azure/azure-dev/cli/azd/pkg/keyvault"
"github.com/azure/azure-dev/cli/azd/pkg/osutil"
"github.com/azure/azure-dev/cli/azd/pkg/prompt"
"github.com/azure/azure-dev/cli/azd/pkg/tools/bicep"
"github.com/azure/azure-dev/cli/azd/test/mocks"
Expand Down Expand Up @@ -384,7 +385,7 @@ func createBicepProvider(t *testing.T, mockContext *mocks.MockContext) *BicepPro
cloud.AzurePublic(),
)

err = provider.Initialize(*mockContext.Context, projectDir, options)
err = provider.Initialize(*mockContext.Context, projectDir, options, osutil.EmptyExpandableString)
require.NoError(t, err)

return provider.(*BicepProvider)
Expand Down
4 changes: 2 additions & 2 deletions cli/azd/pkg/infra/provisioning/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const (
defaultPath = "infra"
)

func (m *Manager) Initialize(ctx context.Context, projectPath string, options Options) error {
func (m *Manager) Initialize(ctx context.Context, projectPath string, options Options, rg osutil.ExpandableString) error {
// applied defaults if missing
if options.Module == "" {
options.Module = defaultModule
Expand All @@ -66,7 +66,7 @@ func (m *Manager) Initialize(ctx context.Context, projectPath string, options Op
}

m.provider = provider
return m.provider.Initialize(ctx, projectPath, options)
return m.provider.Initialize(ctx, projectPath, options, rg)
}

// Gets the latest deployment details for the specified scope
Expand Down
13 changes: 7 additions & 6 deletions cli/azd/pkg/infra/provisioning/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning"
"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning/test"
"github.com/azure/azure-dev/cli/azd/pkg/input"
"github.com/azure/azure-dev/cli/azd/pkg/osutil"
"github.com/azure/azure-dev/cli/azd/pkg/prompt"
"github.com/azure/azure-dev/cli/azd/pkg/tools/azcli"
"github.com/azure/azure-dev/cli/azd/test/mocks"
Expand Down Expand Up @@ -56,7 +57,7 @@ func TestProvisionInitializesEnvironment(t *testing.T) {
nil,
cloud.AzurePublic(),
)
err := mgr.Initialize(*mockContext.Context, "", provisioning.Options{Provider: "test"})
err := mgr.Initialize(*mockContext.Context, "", provisioning.Options{Provider: "test"}, osutil.NewExpandableString(""))
require.NoError(t, err)

require.Equal(t, "00000000-0000-0000-0000-000000000000", env.GetSubscriptionId())
Expand All @@ -83,7 +84,7 @@ func TestManagerPreview(t *testing.T) {
nil,
cloud.AzurePublic(),
)
err := mgr.Initialize(*mockContext.Context, "", provisioning.Options{Provider: "test"})
err := mgr.Initialize(*mockContext.Context, "", provisioning.Options{Provider: "test"}, osutil.NewExpandableString(""))
require.NoError(t, err)

deploymentPlan, err := mgr.Preview(*mockContext.Context)
Expand Down Expand Up @@ -112,7 +113,7 @@ func TestManagerGetState(t *testing.T) {
nil,
cloud.AzurePublic(),
)
err := mgr.Initialize(*mockContext.Context, "", provisioning.Options{Provider: "test"})
err := mgr.Initialize(*mockContext.Context, "", provisioning.Options{Provider: "test"}, osutil.NewExpandableString(""))
require.NoError(t, err)

getResult, err := mgr.State(*mockContext.Context, nil)
Expand Down Expand Up @@ -141,7 +142,7 @@ func TestManagerDeploy(t *testing.T) {
nil,
cloud.AzurePublic(),
)
err := mgr.Initialize(*mockContext.Context, "", provisioning.Options{Provider: "test"})
err := mgr.Initialize(*mockContext.Context, "", provisioning.Options{Provider: "test"}, osutil.NewExpandableString(""))
require.NoError(t, err)

deployResult, err := mgr.Deploy(*mockContext.Context)
Expand Down Expand Up @@ -176,7 +177,7 @@ func TestManagerDestroyWithPositiveConfirmation(t *testing.T) {
nil,
cloud.AzurePublic(),
)
err := mgr.Initialize(*mockContext.Context, "", provisioning.Options{Provider: "test"})
err := mgr.Initialize(*mockContext.Context, "", provisioning.Options{Provider: "test"}, osutil.NewExpandableString(""))
require.NoError(t, err)

destroyOptions := provisioning.NewDestroyOptions(false, false)
Expand Down Expand Up @@ -212,7 +213,7 @@ func TestManagerDestroyWithNegativeConfirmation(t *testing.T) {
nil,
cloud.AzurePublic(),
)
err := mgr.Initialize(*mockContext.Context, "", provisioning.Options{Provider: "test"})
err := mgr.Initialize(*mockContext.Context, "", provisioning.Options{Provider: "test"}, osutil.NewExpandableString(""))
require.NoError(t, err)

destroyOptions := provisioning.NewDestroyOptions(false, false)
Expand Down
5 changes: 3 additions & 2 deletions cli/azd/pkg/infra/provisioning/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package provisioning

import (
"context"

"github.com/azure/azure-dev/cli/azd/pkg/osutil"
)

type ProviderKind string
Expand Down Expand Up @@ -52,10 +54,9 @@ type StateResult struct {

type Provider interface {
Name() string
Initialize(ctx context.Context, projectPath string, options Options) error
Initialize(ctx context.Context, projectPath string, options Options, rg osutil.ExpandableString) error
State(ctx context.Context, options *StateOptions) (*StateResult, error)
Deploy(ctx context.Context) (*DeployResult, error)
Preview(ctx context.Context) (*DeployPreviewResult, error)
Destroy(ctx context.Context, options DestroyOptions) (*DestroyResult, error)
EnsureEnv(ctx context.Context) error
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,12 @@ func NewTerraformProvider(
return provider
}

func (t *TerraformProvider) Initialize(ctx context.Context, projectPath string, options provisioning.Options) error {
func (t *TerraformProvider) Initialize(
ctx context.Context,
projectPath string,
options provisioning.Options,
_ osutil.ExpandableString,
) error {
t.projectPath = projectPath
t.options = options
if t.options.Module == "" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/azure/azure-dev/cli/azd/pkg/environment"
"github.com/azure/azure-dev/cli/azd/pkg/exec"
"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning"
"github.com/azure/azure-dev/cli/azd/pkg/osutil"
"github.com/azure/azure-dev/cli/azd/pkg/prompt"
terraformTools "github.com/azure/azure-dev/cli/azd/pkg/tools/terraform"
"github.com/azure/azure-dev/cli/azd/test/mocks"
Expand Down Expand Up @@ -137,7 +138,7 @@ func createTerraformProvider(t *testing.T, mockContext *mocks.MockContext) *Terr
prompt.NewDefaultPrompter(env, mockContext.Console, accountManager, resourceService, cloud.AzurePublic()),
)

err := provider.Initialize(*mockContext.Context, projectDir, options)
err := provider.Initialize(*mockContext.Context, projectDir, options, osutil.EmptyExpandableString)
require.NoError(t, err)

return provider.(*TerraformProvider)
Expand Down
8 changes: 7 additions & 1 deletion cli/azd/pkg/infra/provisioning/test/test_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/azure/azure-dev/cli/azd/pkg/environment"
"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning"
"github.com/azure/azure-dev/cli/azd/pkg/input"
"github.com/azure/azure-dev/cli/azd/pkg/osutil"
"github.com/azure/azure-dev/cli/azd/pkg/prompt"
"github.com/azure/azure-dev/cli/azd/pkg/tools"
)
Expand All @@ -36,7 +37,12 @@ func (p *TestProvider) RequiredExternalTools() []tools.ExternalTool {
return []tools.ExternalTool{}
}

func (p *TestProvider) Initialize(ctx context.Context, projectPath string, options provisioning.Options) error {
func (p *TestProvider) Initialize(
ctx context.Context,
projectPath string,
options provisioning.Options,
_ osutil.ExpandableString,
) error {
p.projectPath = projectPath
p.options = options

Expand Down
3 changes: 3 additions & 0 deletions cli/azd/pkg/osutil/expandable_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import (
"github.com/drone/envsubst"
)

// EmptyExpandableString is an expandable string that is empty.
var EmptyExpandableString = NewExpandableString("")

func NewExpandableString(template string) ExpandableString {
return ExpandableString{
template: template,
Expand Down

0 comments on commit 781ae4e

Please sign in to comment.