Skip to content

Commit 6f18fa8

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

File tree

4 files changed

+90
-0
lines changed

4 files changed

+90
-0
lines changed

cli/azd/pkg/azapi/resource_service.go

Lines changed: 41 additions & 0 deletions
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,43 @@ func (rs *ResourceService) ListResourceGroup(
167171
return groups, nil
168172
}
169173

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

cli/azd/pkg/azure/arm_template.go

Lines changed: 2 additions & 0 deletions
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

Lines changed: 10 additions & 0 deletions
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

Lines changed: 37 additions & 0 deletions
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,42 @@ func (p *DefaultPrompter) PromptLocation(
111112
return loc, nil
112113
}
113114

115+
func (p *DefaultPrompter) PromptResource(
116+
ctx context.Context,
117+
subId string,
118+
msg string,
119+
resourceType string,
120+
) (string, error) {
121+
options := azapi.ListResourcesOptions{
122+
ResourceType: resourceType,
123+
}
124+
resources, err := p.resourceService.ListResources(ctx, p.env.GetSubscriptionId(), &options)
125+
if err != nil {
126+
return "", fmt.Errorf("listing resources: %w", err)
127+
}
128+
129+
slices.SortFunc(resources, func(a, b *azapi.Resource) int {
130+
return strings.Compare(a.Name, b.Name)
131+
})
132+
133+
// TODO: Add `optional` field to allow something like "Create a new resource" (similar to resources groups below) and return ""?
134+
choices := make([]string, len(resources))
135+
for idx, resource := range resources {
136+
// TODO: Get location display names from account manager instead?
137+
choices[idx] = fmt.Sprintf("%d. %s (%s)", idx+1, resource.Name, resource.Location)
138+
}
139+
140+
choice, err := p.console.Select(ctx, input.ConsoleOptions{
141+
Message: msg,
142+
Options: choices,
143+
})
144+
if err != nil {
145+
return "", fmt.Errorf("selecting resource: %w", err)
146+
}
147+
148+
return resources[choice].Name, nil
149+
}
150+
114151
func (p *DefaultPrompter) PromptResourceGroup(ctx context.Context) (string, error) {
115152
// Get current resource groups
116153
groups, err := p.resourceService.ListResourceGroup(ctx, p.env.GetSubscriptionId(), nil)

0 commit comments

Comments
 (0)