Skip to content

Commit

Permalink
Prompt for resources with optional resourceType
Browse files Browse the repository at this point in the history
Resolves #4530
  • Loading branch information
heaths committed Nov 9, 2024
1 parent f030c4c commit 6f18fa8
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 0 deletions.
41 changes: 41 additions & 0 deletions cli/azd/pkg/azapi/resource_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ type ListResourceGroupResourcesOptions struct {
Filter *string
}

type ListResourcesOptions struct {
ResourceType string
}

type ResourceService struct {
credentialProvider account.SubscriptionCredentialProvider
armClientOptions *arm.ClientOptions
Expand Down Expand Up @@ -167,6 +171,43 @@ func (rs *ResourceService) ListResourceGroup(
return groups, nil
}

func (rs *ResourceService) ListResources(
ctx context.Context,
subscriptionId string,
listOptions *ListResourcesOptions,
) ([]*Resource, error) {
client, err := rs.createResourcesClient(ctx, subscriptionId)
if err != nil {
return nil, err
}

options := armresources.ClientListOptions{}
if listOptions != nil && listOptions.ResourceType != "" {
filter := fmt.Sprintf("resourceType eq '%s'", listOptions.ResourceType)
options.Filter = &filter
}

resources := []*Resource{}
pager := client.NewListPager(&options)

for pager.More() {
page, err := pager.NextPage(ctx)
if err != nil {
return nil, err
}

for _, resource := range page.ResourceListResult.Value {
resources = append(resources, &Resource{
Id: *resource.ID,
Name: *resource.Name,
Type: *resource.Type,
Location: *resource.Location,
})
}
}
return resources, nil
}

func (rs *ResourceService) CreateOrUpdateResourceGroup(
ctx context.Context,
subscriptionId string,
Expand Down
2 changes: 2 additions & 0 deletions cli/azd/pkg/azure/arm_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,13 @@ type AzdMetadataType string
const AzdMetadataTypeLocation AzdMetadataType = "location"
const AzdMetadataTypeGenerate AzdMetadataType = "generate"
const AzdMetadataTypeGenerateOrManual AzdMetadataType = "generateOrManual"
const AzdMetadataTypeResource AzdMetadataType = "resource"

type AzdMetadata struct {
Type *AzdMetadataType `json:"type,omitempty"`
AutoGenerateConfig *AutoGenInput `json:"config,omitempty"`
DefaultValueExpr *string `json:"defaultValueExpr,omitempty"`
ResourceType *string `json:"resourceType,omitempty"`
}

// Description returns the value of the "Description" string metadata for this parameter or empty if it can not be found.
Expand Down
10 changes: 10 additions & 0 deletions cli/azd/pkg/infra/provisioning/bicep/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,16 @@ func (p *BicepProvider) promptForParameter(
}
value = genValue
}
} else if paramType == provisioning.ParameterTypeString &&
azdMetadata.Type != nil &&
*azdMetadata.Type == azure.AzdMetadataTypeResource {

resourceId, err := p.prompters.PromptResource(ctx, p.env.GetSubscriptionId(), msg, *azdMetadata.ResourceType)
if err != nil {
return nil, err
}

value = resourceId
} else if param.AllowedValues != nil {
options := make([]string, 0, len(*param.AllowedValues))
for _, option := range *param.AllowedValues {
Expand Down
37 changes: 37 additions & 0 deletions cli/azd/pkg/prompt/prompter.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type LocationFilterPredicate func(loc account.Location) bool
type Prompter interface {
PromptSubscription(ctx context.Context, msg string) (subscriptionId string, err error)
PromptLocation(ctx context.Context, subId string, msg string, filter LocationFilterPredicate) (string, error)
PromptResource(ctx context.Context, subId string, msg string, resourceType string) (string, error)
PromptResourceGroup(ctx context.Context) (string, error)
}

Expand Down Expand Up @@ -111,6 +112,42 @@ func (p *DefaultPrompter) PromptLocation(
return loc, nil
}

func (p *DefaultPrompter) PromptResource(
ctx context.Context,
subId string,
msg string,
resourceType string,
) (string, error) {
options := azapi.ListResourcesOptions{
ResourceType: resourceType,
}
resources, err := p.resourceService.ListResources(ctx, p.env.GetSubscriptionId(), &options)
if err != nil {
return "", fmt.Errorf("listing resources: %w", err)
}

slices.SortFunc(resources, func(a, b *azapi.Resource) int {
return strings.Compare(a.Name, b.Name)
})

// TODO: Add `optional` field to allow something like "Create a new resource" (similar to resources groups below) and return ""?

Check failure on line 133 in cli/azd/pkg/prompt/prompter.go

View workflow job for this annotation

GitHub Actions / azd-lint (ubuntu-latest)

the line is 132 characters long, which exceeds the maximum of 125 characters. (lll)

Check failure on line 133 in cli/azd/pkg/prompt/prompter.go

View workflow job for this annotation

GitHub Actions / azd-lint (windows-latest)

the line is 132 characters long, which exceeds the maximum of 125 characters. (lll)
choices := make([]string, len(resources))
for idx, resource := range resources {
// TODO: Get location display names from account manager instead?
choices[idx] = fmt.Sprintf("%d. %s (%s)", idx+1, resource.Name, resource.Location)
}

choice, err := p.console.Select(ctx, input.ConsoleOptions{
Message: msg,
Options: choices,
})
if err != nil {
return "", fmt.Errorf("selecting resource: %w", err)
}

return resources[choice].Name, nil
}

func (p *DefaultPrompter) PromptResourceGroup(ctx context.Context) (string, error) {
// Get current resource groups
groups, err := p.resourceService.ListResourceGroup(ctx, p.env.GetSubscriptionId(), nil)
Expand Down

0 comments on commit 6f18fa8

Please sign in to comment.