Skip to content

Commit d14ad7b

Browse files
committed
Prompt for resources with optional resourceType
Resolves #4530
1 parent f030c4c commit d14ad7b

File tree

4 files changed

+97
-0
lines changed

4 files changed

+97
-0
lines changed

cli/azd/pkg/azapi/resource_service.go

+43
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ type ListResourceGroupResourcesOptions struct {
4242
Filter *string
4343
}
4444

45+
type ListResourcesOptions struct {
46+
ResourceType string
47+
}
48+
4549
type ResourceService struct {
4650
credentialProvider account.SubscriptionCredentialProvider
4751
armClientOptions *arm.ClientOptions
@@ -167,6 +171,45 @@ func (rs *ResourceService) ListResourceGroup(
167171
return groups, nil
168172
}
169173

174+
// ListResources returns a slice of resources - optionally filtered on fields in `ListResourcesOptions` - including the
175+
// ID, Name, Type, and Location of each resource.
176+
func (rs *ResourceService) ListResources(
177+
ctx context.Context,
178+
subscriptionId string,
179+
listOptions *ListResourcesOptions,
180+
) ([]*Resource, error) {
181+
client, err := rs.createResourcesClient(ctx, subscriptionId)
182+
if err != nil {
183+
return nil, err
184+
}
185+
186+
options := armresources.ClientListOptions{}
187+
if listOptions != nil && listOptions.ResourceType != "" {
188+
filter := fmt.Sprintf("resourceType eq '%s'", listOptions.ResourceType)
189+
options.Filter = &filter
190+
}
191+
192+
resources := []*Resource{}
193+
pager := client.NewListPager(&options)
194+
195+
for pager.More() {
196+
page, err := pager.NextPage(ctx)
197+
if err != nil {
198+
return nil, err
199+
}
200+
201+
for _, resource := range page.ResourceListResult.Value {
202+
resources = append(resources, &Resource{
203+
Id: *resource.ID,
204+
Name: *resource.Name,
205+
Type: *resource.Type,
206+
Location: *resource.Location,
207+
})
208+
}
209+
}
210+
return resources, nil
211+
}
212+
170213
func (rs *ResourceService) CreateOrUpdateResourceGroup(
171214
ctx context.Context,
172215
subscriptionId string,

cli/azd/pkg/azure/arm_template.go

+2
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,13 @@ type AzdMetadataType string
102102
const AzdMetadataTypeLocation AzdMetadataType = "location"
103103
const AzdMetadataTypeGenerate AzdMetadataType = "generate"
104104
const AzdMetadataTypeGenerateOrManual AzdMetadataType = "generateOrManual"
105+
const AzdMetadataTypeResource AzdMetadataType = "resource"
105106

106107
type AzdMetadata struct {
107108
Type *AzdMetadataType `json:"type,omitempty"`
108109
AutoGenerateConfig *AutoGenInput `json:"config,omitempty"`
109110
DefaultValueExpr *string `json:"defaultValueExpr,omitempty"`
111+
ResourceType *string `json:"resourceType,omitempty"`
110112
}
111113

112114
// Description returns the value of the "Description" string metadata for this parameter or empty if it can not be found.

cli/azd/pkg/infra/provisioning/bicep/prompt.go

+10
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,16 @@ func (p *BicepProvider) promptForParameter(
143143
}
144144
value = genValue
145145
}
146+
} else if paramType == provisioning.ParameterTypeString &&
147+
azdMetadata.Type != nil &&
148+
*azdMetadata.Type == azure.AzdMetadataTypeResource {
149+
150+
resourceId, err := p.prompters.PromptResource(ctx, p.env.GetSubscriptionId(), msg, *azdMetadata.ResourceType)
151+
if err != nil {
152+
return nil, err
153+
}
154+
155+
value = resourceId
146156
} else if param.AllowedValues != nil {
147157
options := make([]string, 0, len(*param.AllowedValues))
148158
for _, option := range *param.AllowedValues {

cli/azd/pkg/prompt/prompter.go

+42
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type LocationFilterPredicate func(loc account.Location) bool
2626
type Prompter interface {
2727
PromptSubscription(ctx context.Context, msg string) (subscriptionId string, err error)
2828
PromptLocation(ctx context.Context, subId string, msg string, filter LocationFilterPredicate) (string, error)
29+
PromptResource(ctx context.Context, subId string, msg string, resourceType string) (string, error)
2930
PromptResourceGroup(ctx context.Context) (string, error)
3031
}
3132

@@ -111,6 +112,47 @@ func (p *DefaultPrompter) PromptLocation(
111112
return loc, nil
112113
}
113114

115+
// PromptResource uses the console (or external) prompter to allow the user to select a resource
116+
// of optional type `resourceType`. The selected resource Name will be returned.
117+
// The Name can be used with the ARM or Bicep template function `reference` or in an existing resource template's name
118+
// to get provisioned state data from a resource, or passed to the `resourceId` function to get the full resource ID
119+
// if you know the `resourceType`.
120+
func (p *DefaultPrompter) PromptResource(
121+
ctx context.Context,
122+
subId string,
123+
msg string,
124+
resourceType string,
125+
) (string, error) {
126+
options := azapi.ListResourcesOptions{
127+
ResourceType: resourceType,
128+
}
129+
resources, err := p.resourceService.ListResources(ctx, p.env.GetSubscriptionId(), &options)
130+
if err != nil {
131+
return "", fmt.Errorf("listing resources: %w", err)
132+
}
133+
134+
slices.SortFunc(resources, func(a, b *azapi.Resource) int {
135+
return strings.Compare(a.Name, b.Name)
136+
})
137+
138+
// TODO: Add `optional` field to allow something like "Create a new resource" (similar to resources groups below) and return ""?
139+
choices := make([]string, len(resources))
140+
for idx, resource := range resources {
141+
// TODO: Get location display names from account manager instead?
142+
choices[idx] = fmt.Sprintf("%d. %s (%s)", idx+1, resource.Name, resource.Location)
143+
}
144+
145+
choice, err := p.console.Select(ctx, input.ConsoleOptions{
146+
Message: msg,
147+
Options: choices,
148+
})
149+
if err != nil {
150+
return "", fmt.Errorf("selecting resource: %w", err)
151+
}
152+
153+
return resources[choice].Name, nil
154+
}
155+
114156
func (p *DefaultPrompter) PromptResourceGroup(ctx context.Context) (string, error) {
115157
// Get current resource groups
116158
groups, err := p.resourceService.ListResourceGroup(ctx, p.env.GetSubscriptionId(), nil)

0 commit comments

Comments
 (0)