Skip to content

Commit

Permalink
Allow app settings to be configured in main.parameters.json for sim…
Browse files Browse the repository at this point in the history
…plified init apps (#2875)

Allow app settings to be configured in `main.parameters.json` for simplified init apps.

To this end, the bicep files in simplified init apps has been updated to allow flowing of any required app settings to the container app host, which enables configuration to be done simply through `main.parameters.json`.

To ease discovery, the generated `main.parameters.json` includes placeholder values and documentation. `next-steps.md` has also been updated to include the information.

Fixes #2739
  • Loading branch information
weikanglim committed Oct 24, 2023
1 parent af84a93 commit e13bfa0
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 10 deletions.
21 changes: 20 additions & 1 deletion cli/azd/internal/scaffold/funcs.go
@@ -1,6 +1,10 @@
package scaffold

import "strings"
import (
"encoding/json"
"fmt"
"strings"
)

// BicepName returns a name suitable for use as a bicep variable name.
//
Expand Down Expand Up @@ -106,3 +110,18 @@ func ContainerAppName(name string) string {

return sb.String()
}

// Formats a parameter value for use in a bicep file.
// If the value is a string, it is quoted inline with no indentation.
// Otherwise, the value is marshaled with indentation specified by prefix and indent.
func FormatParameter(prefix string, indent string, value any) (string, error) {
if valueStr, ok := value.(string); ok {
return fmt.Sprintf("\"%s\"", valueStr), nil
}

val, err := json.MarshalIndent(value, prefix, indent)
if err != nil {
return "", err
}
return string(val), nil
}
3 changes: 3 additions & 0 deletions cli/azd/internal/scaffold/scaffold.go
Expand Up @@ -58,6 +58,7 @@ func Load() (*template.Template, error) {
"containerAppName": ContainerAppName,
"upper": strings.ToUpper,
"lower": strings.ToLower,
"formatParam": FormatParameter,
}

t, err := template.New("templates").
Expand Down Expand Up @@ -164,5 +165,7 @@ func preExecExpand(spec *InfraSpec) {
// containerapp requires a global '_exist' parameter for each service
spec.Parameters = append(spec.Parameters,
containerAppExistsParameter(svc.Name))
spec.Parameters = append(spec.Parameters,
serviceDefPlaceholder(svc.Name))
}
}
42 changes: 41 additions & 1 deletion cli/azd/internal/scaffold/spec.go
Expand Up @@ -16,7 +16,7 @@ type InfraSpec struct {

type Parameter struct {
Name string
Value string
Value any
Type string
Secret bool
}
Expand Down Expand Up @@ -69,3 +69,43 @@ func containerAppExistsParameter(serviceName string) Parameter {
Type: "bool",
}
}

type serviceDef struct {
Settings []serviceDefSettings `json:"settings"`
}

type serviceDefSettings struct {
Name string `json:"name"`
Value string `json:"value"`
Secret bool `json:"secret,omitempty"`
SecretRef string `json:"secretRef,omitempty"`
CommentName string `json:"_comment_name,omitempty"`
CommentValue string `json:"_comment_value,omitempty"`
}

func serviceDefPlaceholder(serviceName string) Parameter {
return Parameter{
Name: BicepName(serviceName) + "Definition",
Value: serviceDef{
Settings: []serviceDefSettings{
{
Name: "",
Value: "${VAR}",
CommentName: "The name of the environment variable when running in Azure. If empty, ignored.",
//nolint:lll
CommentValue: "The value to provide. This can be a fixed literal, or an expression like ${VAR} to use the value of 'VAR' from the current environment.",
},
{
Name: "",
Value: "${VAR_S}",
Secret: true,
CommentName: "The name of the environment variable when running in Azure. If empty, ignored.",
//nolint:lll
CommentValue: "The value to provide. This can be a fixed literal, or an expression like ${VAR_S} to use the value of 'VAR_S' from the current environment.",
},
},
},
Type: "object",
Secret: true,
}
}
30 changes: 26 additions & 4 deletions cli/azd/resources/scaffold/templates/host-containerapp.bicept
Expand Up @@ -25,6 +25,19 @@ param apiUrls array
param allowedOrigins array
{{- end}}
param exists bool
@secure()
param appDefinition object

var appSettingsArray = filter(array(appDefinition.settings), i => i.name != '')
var secrets = map(filter(appSettingsArray, i => i.?secret != null), i => {
name: i.name
value: i.value
secretRef: i.?secretRef ?? take(replace(replace(toLower(i.name), '_', '-'), '.', '-'), 32)
})
var env = map(filter(appSettingsArray, i => i.?secret == null), i => {
name: i.name
value: i.value
})

resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: identityName
Expand Down Expand Up @@ -94,7 +107,7 @@ resource app 'Microsoft.App/containerApps@2023-04-01-preview' = {
identity: identity.id
}
]
secrets: [
secrets: union([
{{- if .DbCosmosMongo}}
{
name: 'azure-cosmos-connection-string'
Expand All @@ -107,14 +120,18 @@ resource app 'Microsoft.App/containerApps@2023-04-01-preview' = {
value: databasePassword
}
{{- end}}
]
],
map(secrets, secret => {
name: secret.secretRef
value: secret.value
}))
}
template: {
containers: [
{
image: fetchLatestImage.outputs.?containers[?0].?image ?? 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
name: 'main'
env: [
env: union([
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: applicationInsights.properties.ConnectionString
Expand Down Expand Up @@ -161,7 +178,12 @@ resource app 'Microsoft.App/containerApps@2023-04-01-preview' = {
value: '{{ .Port }}'
}
{{- end}}
]
],
env,
map(secrets, secret => {
name: secret.name
secretRef: secret.secretRef
}))
resources: {
cpu: json('1.0')
memory: '2.0Gi'
Expand Down
1 change: 1 addition & 0 deletions cli/azd/resources/scaffold/templates/main.bicept
Expand Up @@ -139,6 +139,7 @@ module {{bicepName .Name}} './app/{{.Name}}.bicep' = {
containerAppsEnvironmentName: appsEnv.outputs.name
containerRegistryName: registry.outputs.name
exists: {{bicepName .Name}}Exists
appDefinition: {{bicepName .Name}}Definition
{{- if .DbCosmosMongo}}
cosmosDbConnectionString: vault.getSecret(cosmosDb.outputs.connectionStringKey)
{{- end}}
Expand Down
2 changes: 1 addition & 1 deletion cli/azd/resources/scaffold/templates/main.parameters.jsont
Expand Up @@ -11,7 +11,7 @@
},
{{- range .Parameters}}
"{{.Name}}": {
"value": "{{.Value}}"
"value": {{formatParam " " " " .Value}}
},
{{- end}}
"principalId": {
Expand Down
5 changes: 2 additions & 3 deletions cli/azd/resources/scaffold/templates/next-steps.mdt
Expand Up @@ -12,12 +12,11 @@

### Define environment variables for running services

1. Modify or add environment variables to configure the running application. Environment variables can be configured by modifying the `env` node in the following files:
1. Modify or add environment variables to configure the running application. Environment variables can be configured by updating the `settings` node(s) for each service in [main.parameters.json](./infra/main.parameters.json).
2. For services using a database, environment variables have been pre-configured under the `env` node in the following files to allow connection to the database. Modify the name of these variables as needed to match your application.
{{- range .Services}}
- [app/{{.Name}}.bicep](./infra/app/{{.Name}}.bicep)
{{- end}}
2. For services using a database, environment variables have been pre-configured under `env` to allow connection to the database. Modify the name of these variables as needed to match your application.
3. To set a secret or API key as an environment variable under `env`, the variable should be added with a `secretRef` pointing to a `secrets` entry or a stored KeyVault secret.

### Provision infrastructure and deploy application code

Expand Down

0 comments on commit e13bfa0

Please sign in to comment.