diff --git a/cli/azd/internal/scaffold/funcs.go b/cli/azd/internal/scaffold/funcs.go index d95b9a5e60d..ab394750c11 100644 --- a/cli/azd/internal/scaffold/funcs.go +++ b/cli/azd/internal/scaffold/funcs.go @@ -187,7 +187,7 @@ var camelCaseRegex = regexp.MustCompile(`([a-z0-9])([A-Z])`) // EnvFormat takes an input parameter like `fooParam` which is expected to be in camel case and returns it in // upper snake case with env var template, like `${AZURE_FOO_PARAM}`. func EnvFormat(src string) string { - snake := strings.ToUpper(camelCaseRegex.ReplaceAllString(src, "${1}_${2}")) + snake := strings.ReplaceAll(strings.ToUpper(camelCaseRegex.ReplaceAllString(src, "${1}_${2}")), "-", "_") return fmt.Sprintf("${AZURE_%s}", snake) } diff --git a/cli/azd/pkg/apphost/generate.go b/cli/azd/pkg/apphost/generate.go index 64cc883a3ff..e2078d6effd 100644 --- a/cli/azd/pkg/apphost/generate.go +++ b/cli/azd/pkg/apphost/generate.go @@ -44,6 +44,9 @@ func init() { "fixBackSlash": func(src string) string { return strings.ReplaceAll(src, "\\", "/") }, + "dashToUnderscore": func(src string) string { + return strings.ReplaceAll(src, "-", "_") + }, "envFormat": scaffold.EnvFormat, }, ). @@ -535,12 +538,13 @@ func (b *infraGenerator) addInputParameter(name string, comp *Resource) error { if err != nil { return fmt.Errorf("resolving input for parameter %s: %w", name, err) } + b.bicepContext.InputParameters[name] = input return nil } func hasInputs(value string) bool { - matched, _ := regexp.MatchString(`{[a-zA-Z][a-zA-Z0-9]*\.inputs\.[a-zA-Z][a-zA-Z0-9]*}`, value) + matched, _ := regexp.MatchString(`{[a-zA-Z][a-zA-Z0-9\-]*\.inputs\.[a-zA-Z][a-zA-Z0-9\-]*}`, value) return matched } @@ -638,7 +642,8 @@ func injectValueForBicepParameter(resourceName, p string, parameter any) (string } if p == knownParameterKeyVault { - return fmt.Sprintf("resources.outputs.SERVICE_BINDING_%s_NAME", strings.ToUpper(resourceName+"kv")), true, nil + dashToUnderscore := strings.ReplaceAll(resourceName, "-", "_") + return fmt.Sprintf("resources.outputs.SERVICE_BINDING_%s_NAME", strings.ToUpper(dashToUnderscore+"kv")), true, nil } if p == knownParameterPrincipalId { return knownInjectedValuePrincipalId, true, nil @@ -1316,11 +1321,12 @@ func (b infraGenerator) evalBindingRef(v string, emitType inputEmitType) (string if param.Secret { inputType = "secured-parameter" } + replaceDash := strings.ReplaceAll(resource, "-", "_") switch emitType { case inputEmitTypeBicep: - return fmt.Sprintf("{{%s}}", resource), nil + return fmt.Sprintf("{{%s}}", replaceDash), nil case inputEmitTypeYaml: - return fmt.Sprintf(`{{ %s "%s" }}`, inputType, resource), nil + return fmt.Sprintf(`{{ %s "%s" }}`, inputType, replaceDash), nil default: panic(fmt.Sprintf("unexpected parameter %s", string(emitType))) } diff --git a/cli/azd/pkg/apphost/generate_test.go b/cli/azd/pkg/apphost/generate_test.go index 9edb5355aa8..f243d8a0af7 100644 --- a/cli/azd/pkg/apphost/generate_test.go +++ b/cli/azd/pkg/apphost/generate_test.go @@ -14,6 +14,7 @@ import ( "github.com/azure/azure-dev/cli/azd/pkg/tools/dotnet" "github.com/azure/azure-dev/cli/azd/test/mocks" "github.com/azure/azure-dev/cli/azd/test/snapshot" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -352,6 +353,12 @@ func TestInjectValueForBicepParameter(t *testing.T) { require.Equal(t, expectedParameter, value) require.True(t, inject) + expectedParameter = "resources.outputs.SERVICE_BINDING_EXAMPLE_01KV_NAME" + value, inject, err = injectValueForBicepParameter(resourceName+"-01", param, "") + require.NoError(t, err) + require.Equal(t, expectedParameter, value) + require.True(t, inject) + param = knownParameterPrincipalId expectedParameter = `"exampleParameter"` @@ -439,3 +446,53 @@ func TestInjectValueForBicepParameter(t *testing.T) { require.Equal(t, expectedParameter, value) require.False(t, inject) } + +func TestHasInputs(t *testing.T) { + tests := []struct { + name string + value string + result bool + }{ + { + name: "Valid input with inputs", + value: "{resource.inputs.property}", + result: true, + }, + { + name: "Valid input with inputs dash", + value: "{resource-01.inputs.property}", + result: true, + }, + { + name: "Valid input with numbers", + value: "{resource001.inputs.property}", + result: true, + }, + { + name: "Valid input with numbers and dash", + value: "{resource-01.inputs.property}", + result: true, + }, + { + name: "No inputs - missing inputs token", + value: "{resource.property}", + result: false, + }, + { + name: "No inputs - missing close", + value: "{resource.inputs.property", + result: false, + }, + { + name: "No inputs - unsupported resource", + value: "{resource_001.inputs.property", + result: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.result, hasInputs(tt.value)) + }) + } +} diff --git a/cli/azd/pkg/apphost/testdata/TestAspireBicepGeneration-main.bicep.snap b/cli/azd/pkg/apphost/testdata/TestAspireBicepGeneration-main.bicep.snap index 3759d3660a6..901c1bbf200 100644 --- a/cli/azd/pkg/apphost/testdata/TestAspireBicepGeneration-main.bicep.snap +++ b/cli/azd/pkg/apphost/testdata/TestAspireBicepGeneration-main.bicep.snap @@ -12,7 +12,7 @@ param location string @description('Id of the user or app to assign application roles') param principalId string = '' -param administratorLogin string +param administrator_login string @secure() param administratorLoginPassword string param parameter string @@ -46,16 +46,16 @@ module ai 'ai/aspire.hosting.azure.bicep.appinsights.bicep' = { logAnalyticsWorkspaceId: resources.outputs.AZURE_LOG_ANALYTICS_WORKSPACE_ID } } -module postgres2 'postgres2/aspire.hosting.azure.bicep.postgres.bicep' = { - name: 'postgres2' +module postgres_2 'postgres-2/aspire.hosting.azure.bicep.postgres.bicep' = { + name: 'postgres-2' scope: rg params: { location: location - administratorLogin: administratorLogin + administratorLogin: administrator_login administratorLoginPassword: administratorLoginPassword databases: ['db2'] - keyVaultName: resources.outputs.SERVICE_BINDING_POSTGRES2KV_NAME - serverName: 'postgres2' + keyVaultName: resources.outputs.SERVICE_BINDING_POSTGRES_2KV_NAME + serverName: 'postgres-2' } } module sb 'sb/aspire.hosting.azure.bicep.servicebus.bicep' = { @@ -97,7 +97,7 @@ output AZURE_CONTAINER_REGISTRY_ENDPOINT string = resources.outputs.AZURE_CONTAI output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = resources.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID output AZURE_CONTAINER_APPS_ENVIRONMENT_ID string = resources.outputs.AZURE_CONTAINER_APPS_ENVIRONMENT_ID output AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN string = resources.outputs.AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN -output SERVICE_BINDING_POSTGRES2KV_ENDPOINT string = resources.outputs.SERVICE_BINDING_POSTGRES2KV_ENDPOINT +output SERVICE_BINDING_POSTGRES_2KV_ENDPOINT string = resources.outputs.SERVICE_BINDING_POSTGRES_2KV_ENDPOINT output AI_APPINSIGHTSCONNECTIONSTRING string = ai.outputs.appInsightsConnectionString output SB_SERVICEBUSENDPOINT string = sb.outputs.serviceBusEndpoint diff --git a/cli/azd/pkg/apphost/testdata/TestAspireBicepGeneration-main.parameters.json.snap b/cli/azd/pkg/apphost/testdata/TestAspireBicepGeneration-main.parameters.json.snap index b177ff8c6a3..ac0fdf332ab 100644 --- a/cli/azd/pkg/apphost/testdata/TestAspireBicepGeneration-main.parameters.json.snap +++ b/cli/azd/pkg/apphost/testdata/TestAspireBicepGeneration-main.parameters.json.snap @@ -5,7 +5,7 @@ "principalId": { "value": "${AZURE_PRINCIPAL_ID}" }, - "administratorLogin": { + "administrator_login": { "value": "${AZURE_ADMINISTRATOR_LOGIN}" }, "administratorLoginPassword": { diff --git a/cli/azd/pkg/apphost/testdata/TestAspireBicepGeneration-postgres2-aspire.hosting.azure.bicep.postgres.bicep.snap b/cli/azd/pkg/apphost/testdata/TestAspireBicepGeneration-postgres-2-aspire.hosting.azure.bicep.postgres.bicep.snap similarity index 100% rename from cli/azd/pkg/apphost/testdata/TestAspireBicepGeneration-postgres2-aspire.hosting.azure.bicep.postgres.bicep.snap rename to cli/azd/pkg/apphost/testdata/TestAspireBicepGeneration-postgres-2-aspire.hosting.azure.bicep.postgres.bicep.snap diff --git a/cli/azd/pkg/apphost/testdata/TestAspireBicepGeneration-resources.bicep.snap b/cli/azd/pkg/apphost/testdata/TestAspireBicepGeneration-resources.bicep.snap index acbcc4fbdf9..2847e2c232f 100644 --- a/cli/azd/pkg/apphost/testdata/TestAspireBicepGeneration-resources.bicep.snap +++ b/cli/azd/pkg/apphost/testdata/TestAspireBicepGeneration-resources.bicep.snap @@ -64,7 +64,7 @@ resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' } resource postgres2kv 'Microsoft.KeyVault/vaults@2023-07-01' = { - name: replace('postgres2kv-${resourceToken}', '-', '') + name: replace('postgres-2kv-${resourceToken}', '-', '') location: location properties: { sku: { @@ -105,6 +105,6 @@ output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerRegistry.properties.l output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = managedIdentity.id output AZURE_CONTAINER_APPS_ENVIRONMENT_ID string = containerAppEnvironment.id output AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN string = containerAppEnvironment.properties.defaultDomain -output SERVICE_BINDING_POSTGRES2KV_ENDPOINT string = postgres2kv.properties.vaultUri -output SERVICE_BINDING_POSTGRES2KV_NAME string = postgres2kv.name +output SERVICE_BINDING_POSTGRES_2KV_ENDPOINT string = postgres2kv.properties.vaultUri +output SERVICE_BINDING_POSTGRES_2KV_NAME string = postgres2kv.name diff --git a/cli/azd/pkg/apphost/testdata/aspire-bicep.json b/cli/azd/pkg/apphost/testdata/aspire-bicep.json index 463ea1025bc..e7672ef70a2 100644 --- a/cli/azd/pkg/apphost/testdata/aspire-bicep.json +++ b/cli/azd/pkg/apphost/testdata/aspire-bicep.json @@ -4,7 +4,8 @@ "type": "parameter.v0", "value": "{parameter.inputs.value}", "inputs": { - "value": {} + "value": { + } } }, "test": { @@ -18,9 +19,9 @@ ] } }, - "administratorLogin": { + "administrator-login": { "type": "parameter.v0", - "value": "{administratorLogin.inputs.value}", + "value": "{administrator-login.inputs.value}", "inputs": { "value": { } @@ -31,19 +32,18 @@ "value": "{administratorLoginPassword.inputs.value}", "inputs": { "value": { - "type": "string", "secret": true } } }, - "postgres2": { + "postgres-2": { "type": "azure.bicep.v0", - "connectionString": "{postgres2.secretOutputs.connectionString}", + "connectionString": "{postgres-2.secretOutputs.connectionString}", "path": "aspire.hosting.azure.bicep.postgres.bicep", "params": { - "serverName": "postgres2", + "serverName": "postgres-2", "keyVaultName": "", - "administratorLogin": "{administratorLogin.value}", + "administratorLogin": "{administrator-login.value}", "administratorLoginPassword": "{administratorLoginPassword.value}", "databases": [ "db2" @@ -52,8 +52,8 @@ }, "db2": { "type": "value.v0", - "parent": "postgres2", - "connectionString": "{postgres2.connectionString};Database=db2;" + "parent": "postgres-2", + "connectionString": "{postgres-2.connectionString};Database=db2;" }, "sb": { "type": "azure.bicep.v0", diff --git a/cli/azd/resources/apphost/templates/main.bicept b/cli/azd/resources/apphost/templates/main.bicept index 1ea176b47f7..f335c68b059 100644 --- a/cli/azd/resources/apphost/templates/main.bicept +++ b/cli/azd/resources/apphost/templates/main.bicept @@ -33,7 +33,7 @@ param inputs object {{- if $value.Secret }} @secure() {{- end}} -param {{$param}} {{$value.Type}} +param {{dashToUnderscore $param}} {{$value.Type}} {{- end}} var tags = { @@ -61,7 +61,7 @@ module resources 'resources.bicep' = { } } {{ range $name, $module := .BicepModules }} -module {{$name}} '{{ fixBackSlash $module.Path }}' = { +module {{dashToUnderscore $name}} '{{ fixBackSlash $module.Path }}' = { name: '{{$name}}' scope: rg params: { @@ -115,6 +115,6 @@ output SERVICE_BINDING_{{alphaSnakeUpper $name}}_NAME string = resources.outputs output SERVICE_BINDING_{{alphaSnakeUpper $name}}_NAME string = resources.outputs.SERVICE_BINDING_{{alphaSnakeUpper $name}}_NAME {{end -}} {{- range $param, $value := .OutputParameters}} -output {{$param}} {{$value.Type}} = {{$value.Value}} +output {{dashToUnderscore $param}} {{$value.Type}} = {{dashToUnderscore $value.Value}} {{- end -}} {{ end}} diff --git a/cli/azd/resources/apphost/templates/main.parameters.jsont b/cli/azd/resources/apphost/templates/main.parameters.jsont index 915a4e60836..26777d39e11 100644 --- a/cli/azd/resources/apphost/templates/main.parameters.jsont +++ b/cli/azd/resources/apphost/templates/main.parameters.jsont @@ -9,7 +9,7 @@ }, {{- end }} {{- range $param, $value := .InputParameters}} - "{{$param}}": { + "{{dashToUnderscore $param}}": { "value": "{{ envFormat $param}}" }, {{- end}}