From 591ddf809baf894ddb7136d0ff0f33b2fd538a33 Mon Sep 17 00:00:00 2001 From: Chris Kruining Date: Thu, 21 Nov 2024 10:24:14 +0100 Subject: [PATCH] initial attempt at deployment --- .github/workflows/deploy.yml | 84 ++++++++++ GitVersion.yml | 20 +++ dev.ps1 | 1 + .../environment/containers.bicep | 116 +++++++------- example/containerApps/environment/main.bicep | 19 ++- .../environment/monitoring.bicep | 18 +++ example/containerApps/shared/main.bicep | 7 +- infrastructure/README.md | 3 + infrastructure/main.bicep | 29 ++++ infrastructure/params/main.prd.bicepparam | 1 + infrastructure/registry.bicep | 23 +++ infrastructure/types.bicep | 12 ++ src/base/app-configuration.bicep | 8 +- src/base/app-service.bicep | 15 +- src/base/container-app.bicep | 26 ++-- src/base/container-app/environment.bicep | 147 ++++++++++++++++++ src/base/container-registry.bicep | 6 +- src/base/key-vault.bicep | 8 +- src/base/log-analytics.bicep | 24 +++ src/base/resource-group.bicep | 4 +- src/base/static-web-app.bicep | 8 +- src/base/storage-account.bicep | 14 +- src/common/context.bicep | 12 +- src/common/identity.bicep | 2 +- src/internal/name.bicep | 13 +- src/internal/resource.bicep | 6 +- src/recommended/app-configuration.bicep | 4 +- src/recommended/app-service.bicep | 22 +-- src/recommended/container-app.bicep | 81 +++++++++- src/recommended/container-registry.bicep | 6 +- src/recommended/key-vault.bicep | 6 +- src/recommended/log-analytics.bicep | 5 + src/recommended/resource-group.bicep | 4 +- src/recommended/static-web-app.bicep | 8 +- src/recommended/storage-account.bicep | 12 +- src/types.bicep | 3 + 36 files changed, 632 insertions(+), 145 deletions(-) create mode 100644 .github/workflows/deploy.yml create mode 100644 GitVersion.yml create mode 100644 example/containerApps/environment/monitoring.bicep create mode 100644 infrastructure/README.md create mode 100644 infrastructure/main.bicep create mode 100644 infrastructure/params/main.prd.bicepparam create mode 100644 infrastructure/registry.bicep create mode 100644 infrastructure/types.bicep create mode 100644 src/base/container-app/environment.bicep create mode 100644 src/base/log-analytics.bicep create mode 100644 src/recommended/log-analytics.bicep diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..d28d9c7 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,84 @@ +name: Publish to registry + +on: + push: + branches: + - main + +jobs: + prepare: + name: Calculate next version + runs-on: ubuntu-latest + outputs: + semver: ${{ steps.gitversion.outputs.SemVer }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v3.0.3 + with: + versionSpec: "5.x" + + - name: Determine Version + id: gitversion + uses: gittools/actions/gitversion/execute@v3.0.3 + with: + useConfigFile: true + + infra: + name: Infrastructure + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + sparse-checkout: | + infrastructure + + - name: Az CLI login + uses: azure/login@v2 + with: + client-id: ${{ secrets.TRICEP_CLIENT_ID }} + tenant-id: ${{ secrets.TRICEP_TENANT_ID }} + subscription-id: ${{ secrets.TRICEP_SUBSCRIPTION_ID }} + + - name: Deploy bicep + uses: Azure/cli@v2 + with: + azcliversion: 2.66.0 + inlineScript: | + az deployment sub create \ + --location westeurope \ + --template-file infrastructure/main.bicep \ + --parameters infrastructure/params/main.prd.bicepparam \ + + publish: + name: Publish + runs-on: ubuntu-latest + needs: [ prepare, infra ] + steps: + - uses: actions/checkout@v4 + with: + sparse-checkout: | + src + + - name: Az CLI login + uses: azure/login@v2 + with: + client-id: ${{ secrets.TRICEP_CLIENT_ID }} + tenant-id: ${{ secrets.TRICEP_TENANT_ID }} + subscription-id: ${{ secrets.TRICEP_SUBSCRIPTION_ID }} + + - name: Publish Bicep module + uses: azure/cli@v1 + with: + inlineScript: | + for x in ls -al src; do + echo "$x" + done + + # az bicep publish \ + # --file src/main.bicep \ + # --target 'br:toycompany.azurecr.io/main:${{needs.prepare.outputs.semver}}' diff --git a/GitVersion.yml b/GitVersion.yml new file mode 100644 index 0000000..51a9366 --- /dev/null +++ b/GitVersion.yml @@ -0,0 +1,20 @@ +assembly-versioning-scheme: MajorMinorPatch +assembly-file-versioning-scheme: MajorMinorPatchTag +assembly-informational-format: "{InformationalVersion}" +mode: Mainline +tag-prefix: "[vV]" +continuous-delivery-fallback-tag: ci +major-version-bump-message: '\+semver:\s?(breaking|major)' +minor-version-bump-message: '\+semver:\s?(feature|minor)' +patch-version-bump-message: '\+semver:\s?(fix|patch)' +no-bump-message: '\+semver:\s?(none|skip)' +legacy-semver-padding: 4 +build-metadata-padding: 4 +commits-since-version-source-padding: 4 +commit-message-incrementing: Enabled +branches: {} +ignore: + sha: [] +increment: Inherit +commit-date-format: yyyy-MM-dd +merge-message-formats: {} \ No newline at end of file diff --git a/dev.ps1 b/dev.ps1 index 5ac0edc..a5d5be1 100644 --- a/dev.ps1 +++ b/dev.ps1 @@ -48,4 +48,5 @@ Function Watch { } } +Build Watch diff --git a/example/containerApps/environment/containers.bicep b/example/containerApps/environment/containers.bicep index 364577e..899bf1a 100644 --- a/example/containerApps/environment/containers.bicep +++ b/example/containerApps/environment/containers.bicep @@ -1,17 +1,60 @@ import { Context } from '../../../src/types.bicep' -import { withManagedIdentity } from '../../../src/common/identity.bicep' -import { containerRegistry } from '../../../src/recommended/container-registry.bicep' -import { containerApp, containerAppEnvironment } from '../../../src/recommended/container-app.bicep' +import { with_name } from '../../../src/common/context.bicep' +import { with_managed_identity } from '../../../src/common/identity.bicep' +import { container_registry } from '../../../src/recommended/container-registry.bicep' +import { + container_app + container + with_dapr + container_app_environment + with_auto_scaling + with_environment + with_app_logs +} from '../../../src/recommended/container-app.bicep' targetScope = 'resourceGroup' param context Context +param customerId string +param sharedKey string -var containerRegistryConfig = containerRegistry(context, []) -var containerAppEnvironmentConfig = containerAppEnvironment(context, []) -var containerAppConfig = containerApp(context, [ - withManagedIdentity() +var containerRegistryConfig = container_registry(with_name(context, context.project), []) +var containerAppEnvironmentConfig = container_app_environment(with_name(context, context.project), [ + with_app_logs(customerId, sharedKey) ]) +var containerApp1Config = container_app( + with_name(context, 'app_1'), + [ + container('container 1', 'some-container-image') + container('container 2', 'some-other-container-image') + ], + [ + with_managed_identity() + with_environment(environment.id) + with_dapr(context, 3000) + with_auto_scaling(0, 1, { + ruleName: { + concurrentRequests: '10' + } + }) + ] +) +var containerApp2Config = container_app( + with_name(context, 'app_2'), + [ + container('container 1', 'some-third-container-image') + ], + [ + with_managed_identity() + with_environment(environment.id) + with_dapr(context, 3001) + with_auto_scaling(1, 1, { + ruleName: { + concurrentRequests: '10' + } + }) + ] +) resource registry 'Microsoft.ContainerRegistry/registries@2023-07-01' = { name: containerRegistryConfig.name @@ -25,28 +68,16 @@ resource environment 'Microsoft.App/managedEnvironments@2024-03-01' = { name: containerAppEnvironmentConfig.name location: containerAppEnvironmentConfig.location tags: containerAppEnvironmentConfig.tags - sku: containerAppEnvironmentConfig.sku properties: containerAppEnvironmentConfig.properties - - // properties: { - // appLogsConfiguration: { - // destination: 'log-analytics' - // logAnalyticsConfiguration: { - // customerId: logAnalytics.properties.customerId - // sharedKey: logAnalytics.listKeys().primarySharedKey - // } - // } - // } } -resource app 'Microsoft.App/containerApps@2024-03-01' = { - name: containerAppConfig.name - location: containerAppConfig.location - identity: containerAppConfig.identity - properties: containerAppConfig.properties +resource app_1 'Microsoft.App/containerApps@2024-03-01' = { + name: containerApp1Config.name + location: containerApp1Config.location + identity: containerApp1Config.identity + properties: containerApp1Config.properties // properties: { - // managedEnvironmentId: containerAppEnv.id // configuration: { // ingress: { // external: true @@ -66,34 +97,15 @@ resource app 'Microsoft.App/containerApps@2024-03-01' = { // } // ] // } - // template: { - // revisionSuffix: 'firstrevision' - // containers: [ - // { - // name: containerAppName - // image: acrImportImage.outputs.importedImages[0].acrHostedImage - // resources: { - // cpu: json('.25') - // memory: '.5Gi' - // } - // } - // ] - // scale: { - // minReplicas: minReplica - // maxReplicas: maxReplica - // rules: [ - // { - // name: 'http-requests' - // http: { - // metadata: { - // concurrentRequests: '10' - // } - // } - // } - // ] - // } - // } // } } -output app resource'Microsoft.App/containerApps@2022-06-01-preview' = app +resource app_2 'Microsoft.App/containerApps@2024-03-01' = { + name: containerApp2Config.name + location: containerApp2Config.location + identity: containerApp2Config.identity + properties: containerApp2Config.properties +} + +output app_1 resource'Microsoft.App/containerApps@2022-06-01-preview' = app_1 +output app_2 resource'Microsoft.App/containerApps@2022-06-01-preview' = app_2 diff --git a/example/containerApps/environment/main.bicep b/example/containerApps/environment/main.bicep index c7b2f62..cbb9a0c 100644 --- a/example/containerApps/environment/main.bicep +++ b/example/containerApps/environment/main.bicep @@ -1,14 +1,15 @@ import { Context } from '../../../src/types.bicep' -import { createContext } from '../../../src/common/context.bicep' -import { resourceGroup } from '../../../src/recommended/resource-group.bicep' +import { create_context } from '../../../src/common/context.bicep' +import { resource_group } from '../../../src/recommended/resource-group.bicep' targetScope = 'subscription' param deployedAt string param environment string -var context = createContext({ +var context = create_context({ name: 'appName' + project: 'project' nameConventionTemplate: '$type-$env-$loc-$name' environment: environment location: 'westeurope' @@ -17,7 +18,7 @@ var context = createContext({ tags: {} }) -var resourceGroupConfig = resourceGroup(context, []) +var resourceGroupConfig = resource_group(context, []) resource group 'Microsoft.Resources/resourceGroups@2024-07-01' = { name: resourceGroupConfig.name @@ -26,10 +27,20 @@ resource group 'Microsoft.Resources/resourceGroups@2024-07-01' = { properties: resourceGroupConfig.properties } +module monitoring 'monitoring.bicep' = { + name: 'monitoring' + scope: group + params: { + context: context + } +} + module containers 'containers.bicep' = { name: 'containers' scope: group params: { context: context + customerId: 'monitoring.outputs.la_customerId' + sharedKey: 'monitoring.outputs.la_sharedKey' } } diff --git a/example/containerApps/environment/monitoring.bicep b/example/containerApps/environment/monitoring.bicep new file mode 100644 index 0000000..bd64776 --- /dev/null +++ b/example/containerApps/environment/monitoring.bicep @@ -0,0 +1,18 @@ +import { Context } from '../../../src/types.bicep' +import { log_analytics } from '../../../src/recommended/log-analytics.bicep' + +targetScope = 'resourceGroup' + +param context Context + +var logAnalyticsConfig = log_analytics(context, []) + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { + name: logAnalyticsConfig.name + location: logAnalyticsConfig.location + properties: logAnalyticsConfig.properties +} + +// output logAnalytics resource'Microsoft.OperationalInsights/workspaces@2021-06-01' = logAnalytics +output la_customerId string = logAnalytics.properties.customerId +output la_sharedKey string = logAnalytics.listKeys().primarySharedKey diff --git a/example/containerApps/shared/main.bicep b/example/containerApps/shared/main.bicep index 1df604f..c4be5b1 100644 --- a/example/containerApps/shared/main.bicep +++ b/example/containerApps/shared/main.bicep @@ -1,15 +1,16 @@ -import { createContext } from '../../../src/common/context.bicep' -import { resourceGroup } from '../../../src/recommended/resource-group.bicep' +import { create_context } from '../../../src/common/context.bicep' +import { resource_group } from '../../../src/recommended/resource-group.bicep' targetScope = 'subscription' param deployedAt string -var context = createContext({ +var context = create_context({ name: 'appName' nameConventionTemplate: '$type-$env-$loc-$name' environment: 'shared' location: 'westeurope' deployedAt: deployedAt tenant: tenant() + tags: {} }) diff --git a/infrastructure/README.md b/infrastructure/README.md new file mode 100644 index 0000000..34ea6e3 --- /dev/null +++ b/infrastructure/README.md @@ -0,0 +1,3 @@ + + +# this is not part of the library, this is the bicep used to deploy the bicep \ No newline at end of file diff --git a/infrastructure/main.bicep b/infrastructure/main.bicep new file mode 100644 index 0000000..0c3cdc3 --- /dev/null +++ b/infrastructure/main.bicep @@ -0,0 +1,29 @@ +targetScope = 'subscription' + +param deployedAt string = utcNow('yyyyMMdd') + +var locationAbbreviation = 'euw' +var location = 'west europe' +var environment = 'prd' +var projectName = 'tricep' + +var context = { + locationAbbreviation: locationAbbreviation + location: location + environment: environment + projectName: projectName + deployedAt: deployedAt +} + +resource calqueResourceGroup 'Microsoft.Resources/resourceGroups@2024-07-01' = { + name: 'rg-${locationAbbreviation}-${environment}-${projectName}' + location: location +} + +module registry 'registry.bicep' = { + name: 'registry' + scope: calqueResourceGroup + params: { + context: context + } +} diff --git a/infrastructure/params/main.prd.bicepparam b/infrastructure/params/main.prd.bicepparam new file mode 100644 index 0000000..550f874 --- /dev/null +++ b/infrastructure/params/main.prd.bicepparam @@ -0,0 +1 @@ +using '../main.bicep' diff --git a/infrastructure/registry.bicep b/infrastructure/registry.bicep new file mode 100644 index 0000000..a13abbf --- /dev/null +++ b/infrastructure/registry.bicep @@ -0,0 +1,23 @@ +import { Context } from 'types.bicep' + +targetScope = 'resourceGroup' + +param context Context + +resource registry 'Microsoft.ContainerRegistry/registries@2023-07-01' = { + name: 'acr${context.locationAbbreviation}${context.environment}${context.projectName}' + location: context.location + sku: { + name: 'Basic' + } + identity: { + type: 'SystemAssigned' + } + properties: { + adminUserEnabled: true + dataEndpointEnabled: false + encryption: { + status: 'disabled' + } + } +} diff --git a/infrastructure/types.bicep b/infrastructure/types.bicep new file mode 100644 index 0000000..768ff80 --- /dev/null +++ b/infrastructure/types.bicep @@ -0,0 +1,12 @@ +@export() +type Context = { + @minLength(2) + locationAbbreviation: string + @minLength(2) + location: string + @minLength(3) + environment: string + @minLength(2) + projectName: string + deployedAt: string +} diff --git a/src/base/app-configuration.bicep b/src/base/app-configuration.bicep index 4e5f06a..5342a1e 100644 --- a/src/base/app-configuration.bicep +++ b/src/base/app-configuration.bicep @@ -1,9 +1,9 @@ -import { createResource } from '../internal/resource.bicep' +import { create_resource } from '../internal/resource.bicep' import { Context, Options } from '../types.bicep' @export() -func appConfiguration(context Context, sku Sku, options Options) object => - createResource( +func app_configuration(context Context, sku Sku, options Options) object => + create_resource( context, 'appConfiguration', union( @@ -20,7 +20,7 @@ func appConfiguration(context Context, sku Sku, options Options) object => // Sku @export() -func withSku(sku Sku) object => { +func with_sku(sku Sku) object => { sku: { name: sku } diff --git a/src/base/app-service.bicep b/src/base/app-service.bicep index f63e4dc..fb15f3a 100644 --- a/src/base/app-service.bicep +++ b/src/base/app-service.bicep @@ -1,13 +1,12 @@ -import { createResource } from '../internal/resource.bicep' +import { create_resource } from '../internal/resource.bicep' import { Context, Options } from '../types.bicep' @export() -func appService(context Context, options Options) object => createResource(context, 'appService', options) - +func app_service(context Context, options Options) object => create_resource(context, 'appService', options) // Always on @export() -func withAlwaysOn(alwaysOn bool) object => { +func with_always_on(alwaysOn bool) object => { properties: { siteConfig: { alwaysOn: alwaysOn @@ -17,7 +16,7 @@ func withAlwaysOn(alwaysOn bool) object => { // Site configuration @export() -func withSiteConfig( +func with_site_config( appServicePlan string, vnetName string, vnetRouteAllEnabled bool, @@ -47,7 +46,7 @@ type IpSecurityRestrictionAction = 'Allow' | 'Deny' // App settings @export() -func withIpRestrictions(ipRestrictions IpSecurityRestriction[]) object => { +func with_ip_restrictions(ipRestrictions IpSecurityRestriction[]) object => { properties: { siteConfig: { ipSecurityRestrictions: ipRestrictions @@ -65,7 +64,7 @@ type IpSecurityRestriction = { // App settings @export() -func withAppSettings(appSettings AppSetting[]) object => { +func with_app_settings(appSettings AppSetting[]) object => { properties: { siteConfig: { appSettings: appSettings @@ -81,7 +80,7 @@ type AppSetting = { // Connections strings @export() -func withConnectionStrings(connectionStrings ConnStringInfo[]) object => { +func with_connection_strings(connectionStrings ConnStringInfo[]) object => { properties: { siteConfig: { connectionStrings: connectionStrings diff --git a/src/base/container-app.bicep b/src/base/container-app.bicep index 992fe4a..c605ffb 100644 --- a/src/base/container-app.bicep +++ b/src/base/container-app.bicep @@ -1,24 +1,27 @@ -import { createResource } from '../internal/resource.bicep' +import { create_resource } from '../internal/resource.bicep' import { Context, Options, Tags } from '../types.bicep' -@export() -func containerApp(context Context, options Options) ContainerApp => createResource(context, 'containerApp', options) +import { container_app_environment } from 'container-app/environment.bicep' @export() -func containerAppEnvironment(context Context, sku Sku, options Options) ContainerApp => - createResource( +func container_app(context Context, containers Container[], options Options) object => + create_resource( context, - 'containerAppEnvironment', - concat( + 'containerApp', + union( [ - { sku: { name: sku } } + { + properties: { + template: { + containers: containers + } + } + } ], options ) ) -type Sku = 'Consumption' - @export() type ContainerApp = { @description('The resource name.') @@ -300,6 +303,7 @@ type Template = { volumes: Volume[]? } +@export() type Container = { @description('Container start command arguments.') args: string[]? @@ -401,7 +405,7 @@ type ContainerAppProbeTcpSocket = { type ContainerResources = { @description('Required CPU in cores, e.g. 0.5 To specify a decimal value, use the json() function.') - cpu: int? + cpu: string? @description('Required memory, e.g. "250Mb".') memory: string? diff --git a/src/base/container-app/environment.bicep b/src/base/container-app/environment.bicep new file mode 100644 index 0000000..01f6a48 --- /dev/null +++ b/src/base/container-app/environment.bicep @@ -0,0 +1,147 @@ +import { create_resource } from '../../internal/resource.bicep' +import { Context, Options, Tags } from '../../types.bicep' + +@export() +func container_app_environment(context Context, options Options) object => + create_resource( + context, + 'containerAppEnvironment', + concat( + [ + {} + ], + options + ) + ) + +type ManagedEnvironments = { + name: string + location: string + tags: { *: string } + kind: string + properties: ManagedEnvironmentProperties +} + +type ManagedEnvironmentProperties = { + @description('Cluster configuration which enables the log daemon to export app logs to a destination. Currently only "log-analytics" is supported.') + appLogsConfiguration: AppLogsConfiguration + + @description('Custom domain configuration for the environment.') + customDomainConfiguration: CustomDomainConfiguration + + @description('Application Insights connection string used by Dapr to export Service to Service communication telemetry.') + @secure() + daprAIConnectionString: string + + @description('Azure Monitor instrumentation key used by Dapr to export Service to Service communication telemetry.') + @secure() + daprAIInstrumentationKey: string + + @description('The configuration of Dapr component.') + daprConfiguration: DaprConfiguration + + @description('Name of the platform-managed resource group created for the Managed Environment to host infrastructure resources. If a subnet ID is provided, this resource group will be created in the same subscription as the subnet.') + infrastructureResourceGroup: string + + @description('The configuration of Keda component.') + kedaConfiguration: KedaConfiguration + + @description('Peer authentication settings for the Managed Environment.') + peerAuthentication: ManagedEnvironmentPropertiesPeerAuthentication + + @description('Peer traffic settings for the Managed Environment.') + peerTrafficConfiguration: ManagedEnvironmentPropertiesPeerTrafficConfiguration + + @description('Vnet configuration for the environment.') + vnetConfiguration: VnetConfiguration + + @description('Workload profiles configured for the Managed Environment.') + workloadProfiles: WorkloadProfile[] + + @description('Whether or not this Managed Environment is zone-redundant.') + zoneRedundant: bool +} + +type AppLogsConfiguration = { + @description('Logs destination, can be \'log-analytics\', \'azure-monitor\' or \'none\'.') + destination: 'log-analytics' | 'azure-monitor' | 'none' + + @description('Log Analytics configuration, must only be provided when destination is configured as \'log-analytics\'.') + logAnalyticsConfiguration: LogAnalyticsConfiguration +} + +type LogAnalyticsConfiguration = { + @description('Log analytics customer id.') + customerId: string + + @description('Log analytics customer key.') + @secure() + sharedKey: string +} + +type CustomDomainConfiguration = { + @description('Certificate password.') + @secure() + certificatePassword: string + + @description('PFX or PEM blob (DO NOT USE, string is not the proper type).') + certificateValue: string + + @description('Dns suffix for the environment domain.') + dnsSuffix: string +} + +type DaprConfiguration = {} + +type KedaConfiguration = {} + +type ManagedEnvironmentPropertiesPeerAuthentication = { + @description('Mutual TLS authentication settings for the Managed Environment.') + mtls: Mtls +} + +type Mtls = { + @description('Boolean indicating whether the mutual TLS authentication is enabled.') + enabled: bool +} + +type ManagedEnvironmentPropertiesPeerTrafficConfiguration = { + @description('Peer traffic encryption settings for the Managed Environment.') + encryption: ManagedEnvironmentPropertiesPeerTrafficConfigurationEncryption +} + +type ManagedEnvironmentPropertiesPeerTrafficConfigurationEncryption = { + @description('Boolean indicating whether the peer traffic encryption is enabled.') + enabled: bool +} + +type VnetConfiguration = { + @description('CIDR notation IP range assigned to the Docker bridge, network. Must not overlap with any other provided IP ranges.') + dockerBridgeCidr: string + + @description('Resource ID of a subnet for infrastructure components. Must not overlap with any other provided IP ranges.') + infrastructureSubnetId: string + + @description('Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. They must provide infrastructureSubnetId if enabling this property.') + internal: bool + + @description('IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. Must not overlap with any other provided IP ranges.') + platformReservedCidr: string + + @description('An IP address from the IP range defined by platformReservedCidr that will be reserved for the internal DNS server.') + platformReservedDnsIP: string +} + +type WorkloadProfile = { + @description('The maximum capacity.') + maximumCount: int? + + @description('The minimum capacity.') + minimumCount: int? + + @description('name of the workload') + name: string + + @description('Workload profile type for the workloads to run on.') + workloadProfileType: string +} diff --git a/src/base/container-registry.bicep b/src/base/container-registry.bicep index 68edd03..7a3b3e5 100644 --- a/src/base/container-registry.bicep +++ b/src/base/container-registry.bicep @@ -1,9 +1,9 @@ import { Context, Options } from '../types.bicep' -import { createResource } from '../internal/resource.bicep' +import { create_resource } from '../internal/resource.bicep' @export() -func containerRegistry(context Context, sku Sku, options Options) object => - createResource( +func container_registry(context Context, sku Sku, options Options) object => + create_resource( context, 'containerRegistry', concat( diff --git a/src/base/key-vault.bicep b/src/base/key-vault.bicep index 40d8fca..68712d3 100644 --- a/src/base/key-vault.bicep +++ b/src/base/key-vault.bicep @@ -1,10 +1,10 @@ -import { createResource } from '../internal/resource.bicep' +import { create_resource } from '../internal/resource.bicep' import { Context, Options } from '../types.bicep' @description('key vault') @export() -func keyVault(context Context, sku Sku, options Options) object => - createResource( +func key_vault(context Context, sku Sku, options Options) object => + create_resource( context, 'keyVault', union( @@ -31,7 +31,7 @@ type Sku = 'premium' | 'standard' // Soft delete @export() -func withSoftDelete(enable bool) object => { +func with_soft_delete(enable bool) object => { properties: { enableSoftDelete: enable } diff --git a/src/base/log-analytics.bicep b/src/base/log-analytics.bicep new file mode 100644 index 0000000..7ad8b24 --- /dev/null +++ b/src/base/log-analytics.bicep @@ -0,0 +1,24 @@ +import { create_resource } from '../internal/resource.bicep' +import { Context, Options } from '../types.bicep' + +@export() +func log_analytics(context Context, sku Sku, options Options) object => + create_resource( + context, + 'logAnalytics', + union( + [ + { + properties: { + sku: { + name: sku + } + } + } + ], + options + ) + ) + +@export() +type Sku = 'PerGB2018' diff --git a/src/base/resource-group.bicep b/src/base/resource-group.bicep index 1427961..d6ac355 100644 --- a/src/base/resource-group.bicep +++ b/src/base/resource-group.bicep @@ -1,5 +1,5 @@ import { Context, Options } from '../types.bicep' -import { createResource } from '../internal/resource.bicep' +import { create_resource } from '../internal/resource.bicep' @export() -func resourceGroup(context Context, options Options) object => createResource(context, 'resourceGroup', options) +func resource_group(context Context, options Options) object => create_resource(context, 'resourceGroup', options) diff --git a/src/base/static-web-app.bicep b/src/base/static-web-app.bicep index 7b0c31d..501056c 100644 --- a/src/base/static-web-app.bicep +++ b/src/base/static-web-app.bicep @@ -1,12 +1,12 @@ -import { createResource } from '../internal/resource.bicep' +import { create_resource } from '../internal/resource.bicep' import { Context, Options } from '../types.bicep' @export() -func staticWebApp(context Context, options Options) object => createResource(context, 'staticWebApp', options) +func static_web_app(context Context, options Options) object => create_resource(context, 'staticWebApp', options) // Identity @export() -func withIdentity(identity Identity) object => { +func with_identity(identity Identity) object => { identity: { type: identity } @@ -17,7 +17,7 @@ type Identity = 'SystemAssigned' | 'Manual' // Sku @export() -func withSku(sku Sku) object => { +func with_sku(sku Sku) object => { sku: { name: sku tier: sku diff --git a/src/base/storage-account.bicep b/src/base/storage-account.bicep index b81b5cf..bb75875 100644 --- a/src/base/storage-account.bicep +++ b/src/base/storage-account.bicep @@ -1,9 +1,9 @@ -import { createResource } from '../internal/resource.bicep' +import { create_resource } from '../internal/resource.bicep' import { Context, Options } from '../types.bicep' @export() -func storageAccount(context Context, sku Sku, kind Kind, options Options) object => - createResource( +func storage_account(context Context, sku Sku, kind Kind, options Options) object => + create_resource( context, 'storageAccount', union( @@ -22,7 +22,7 @@ func storageAccount(context Context, sku Sku, kind Kind, options Options) object // Sku @export() -func withSku(sku Sku) object => { +func with_sku(sku Sku) object => { sku: sku } @@ -39,7 +39,7 @@ type Sku = // Kind @export() -func withKind(kind Kind) object => { +func with_kind(kind Kind) object => { kind: kind } @@ -48,7 +48,7 @@ type Kind = 'BlobStorage' | 'BlockBlobStorage' | 'FileStorage' | 'Storage' | 'St // AccessTier @export() -func withAccessTier(accessTier AccessTier) object => { +func with_access_tier(accessTier AccessTier) object => { properties: { accessTier: accessTier } @@ -59,7 +59,7 @@ type AccessTier = 'Cool' | 'Hot' | 'Premium' // AllowPublicAccess @export() -func withAllowPublicAccess(allowPublicAccess bool) object => { +func with_allow_public_access(allowPublicAccess bool) object => { properties: { allowPublicAccess: allowPublicAccess } diff --git a/src/common/context.bicep b/src/common/context.bicep index 5aec303..309f1c7 100644 --- a/src/common/context.bicep +++ b/src/common/context.bicep @@ -1,8 +1,9 @@ import { Context, Location, Tags, Tenant } from '../types.bicep' @export() -func createContext(params CreateContextParams) Context => { +func create_context(params create_contextParams) Context => { name: params.name + project: params.project nameConventionTemplate: params.nameConventionTemplate environment: params.environment location: params.location @@ -17,8 +18,9 @@ func createContext(params CreateContextParams) Context => { } } -type CreateContextParams = { +type create_contextParams = { name: string + project: string nameConventionTemplate: string deployedAt: string environment: string @@ -26,3 +28,9 @@ type CreateContextParams = { tenant: Tenant tags: Tags } + +@export() +func with_name(context Context, name string) Context => { + ...context + name: name +} diff --git a/src/common/identity.bicep b/src/common/identity.bicep index 54f83a2..d8592cf 100644 --- a/src/common/identity.bicep +++ b/src/common/identity.bicep @@ -1,5 +1,5 @@ @export() -func withManagedIdentity() object => { +func with_managed_identity() object => { identity: { type: 'SystemAssigned' } diff --git a/src/internal/name.bicep b/src/internal/name.bicep index 3d951d4..3a12b31 100644 --- a/src/internal/name.bicep +++ b/src/internal/name.bicep @@ -1,8 +1,8 @@ import { ResourceType, Abbreviation, Location } from '../types.bicep' @export() -func createName(context object, template string) string => - reduce(items(context), template, (t, e) => replace(string(t), '$${e.key}', e.value)) +func create_name(context object, template string) string => + toLower(reduce(items(context), template, (t, e) => replace(string(t), '$${e.key}', e.value))) @export() func to_resource_abbreviation(resourceType ResourceType) Abbreviation => @@ -14,7 +14,16 @@ func to_resource_abbreviation(resourceType ResourceType) Abbreviation => managedIdentity: 'id' staticWebApp: 'stapp' appService: 'app' + appServicePlan: 'asp' containerRegistry: 'cr' + appConfiguration: 'appcs' + serviceBus: 'sb' + serviceBusQueue: 'sbq' + serviceBusTopic: 'sbt' + serviceBusTopicSubscription: 'sbts' + applicationInsights: 'appi' + storageAccount: 'st' + logAnalytics: 'la' }[resourceType] @export() diff --git a/src/internal/resource.bicep b/src/internal/resource.bicep index 900ae70..4be788b 100644 --- a/src/internal/resource.bicep +++ b/src/internal/resource.bicep @@ -1,12 +1,12 @@ import { Context, ResourceType } from '../types.bicep' -import { createName, to_resource_abbreviation, to_location_abbreviation } from './name.bicep' +import { create_name, to_resource_abbreviation, to_location_abbreviation } from './name.bicep' @export() -func createResource(context Context, resourceType ResourceType, options object[]) object => +func create_resource(context Context, resourceType ResourceType, options object[]) object => reduce( options, { - name: createName( + name: create_name( { env: context.environment loc: to_location_abbreviation(context.location) diff --git a/src/recommended/app-configuration.bicep b/src/recommended/app-configuration.bicep index b4a33ac..4292faf 100644 --- a/src/recommended/app-configuration.bicep +++ b/src/recommended/app-configuration.bicep @@ -2,7 +2,7 @@ import * as base from '../base/app-configuration.bicep' import { Context, Options } from '../types.bicep' @export() -func appConfiguration(context Context, options Options) object => base.appConfiguration(context, 'Standard', options) +func app_configuration(context Context, options Options) object => base.app_configuration(context, 'Standard', options) @export() -func withFreeSku() object => base.withSku('Free') +func withFreeSku() object => base.with_sku('Free') diff --git a/src/recommended/app-service.bicep b/src/recommended/app-service.bicep index 2a193ea..a807020 100644 --- a/src/recommended/app-service.bicep +++ b/src/recommended/app-service.bicep @@ -1,14 +1,14 @@ -import * as common from '../base/app-service.bicep' -import { Context } from '../types.bicep' +import * as base from '../base/app-service.bicep' +import { Context, Options } from '../types.bicep' @export() func appService(context Context, appServicePlan string, vnetName string, appInsights string, options Options) object => - common.appService( + base.appService( context, union( [ - common.withSiteConfig(appServicePlan, vnetName, true, true, '1.2', 'Deny', false) - common.withAppSettings([ + base.withSiteConfig(appServicePlan, vnetName, true, true, '1.2', 'Deny', false) + base.withAppSettings([ { name: 'ASPNETCORE_ENVIRONMENT' value: context.environment @@ -22,7 +22,7 @@ func appService(context Context, appServicePlan string, vnetName string, appInsi value: '~2' } ]) - common.withIpRestrictions([ + base.withIpRestrictions([ { name: 'Allow backend subnet ${context.environment}' vnetSubnetResourceId: vnetName @@ -36,18 +36,18 @@ func appService(context Context, appServicePlan string, vnetName string, appInsi ) @export() -func withAlwaysOn(alwaysOn bool) object => common.withAlwaysOn(alwaysOn) +func withAlwaysOn(alwaysOn bool) object => base.withAlwaysOn(alwaysOn) @export() -func withAppSettings(appSettings common.AppSetting[]) object => common.withAppSettings(appSettings) +func withAppSettings(appSettings base.AppSetting[]) object => base.withAppSettings(appSettings) @export() -func withConnectionStrings(connectionStrings common.ConnStringInfo[]) object => - common.withConnectionStrings(connectionStrings) +func withConnectionStrings(connectionStrings base.ConnStringInfo[]) object => + base.withConnectionStrings(connectionStrings) @export() func withAllowManagementVm(managementSubNet string) object => - common.withIpRestrictions([ + base.withIpRestrictions([ { name: 'Allow management vm' vnetSubnetResourceId: managementSubNet diff --git a/src/recommended/container-app.bicep b/src/recommended/container-app.bicep index 03c9c38..866c967 100644 --- a/src/recommended/container-app.bicep +++ b/src/recommended/container-app.bicep @@ -1,9 +1,82 @@ import { Context, Options } from '../types.bicep' -import * as common from '../base/container-app.bicep' +import { create_name, to_location_abbreviation, to_resource_abbreviation } from '../internal/name.bicep' +import * as base from '../base/container-app.bicep' +import { container_app_environment as base_container_app_environment } from '../base/container-app/environment.bicep' @export() -func containerApp(context Context, options Options) object => common.containerApp(context, options) +func container_app(context Context, containers base.Container[], options Options) object => + base.container_app(context, containers, options) @export() -func containerAppEnvironment(context Context, options Options) object => - common.containerAppEnvironment(context, 'Consumption', options) +func container(name string, image string) base.Container => { + name: name + image: image + resources: { + cpu: '0.5' + memory: '0.5Gi' + } +} + +@export() +func with_environment(id string) object => { + properties: { + environmentId: id + } +} + +@export() +func with_dapr(context Context, port int) object => { + properties: { + configuration: { + dapr: { + appId: create_name( + { + env: context.environment + loc: to_location_abbreviation(context.location) + type: to_resource_abbreviation('containerApp') + name: context.name + }, + context.nameConventionTemplate + ) + appPort: port + enabled: true + enabledApiLogging: true + } + } + } +} + +@export() +func with_auto_scaling(min int, max int, rules { *: object }) object => { + properties: { + template: { + scale: { + minReplicas: min + maxReplicas: max + rules: map(items(rules), (rule) => { + name: rule.key + http: { + metadata: rule.value + } + }) + } + } + } +} + +@export() +func container_app_environment(context Context, options Options) object => + base_container_app_environment(context, options) + +@export() +func with_app_logs(customerId string, sharedKey string) object => { + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: customerId + sharedKey: sharedKey + } + } + } +} diff --git a/src/recommended/container-registry.bicep b/src/recommended/container-registry.bicep index 49174ca..cad1433 100644 --- a/src/recommended/container-registry.bicep +++ b/src/recommended/container-registry.bicep @@ -1,6 +1,6 @@ import { Context, Options } from '../types.bicep' -import * as common from '../base/container-registry.bicep' +import * as base from '../base/container-registry.bicep' @export() -func containerRegistry(context Context, options Options) object => - common.containerRegistry(context, 'Standard', options) +func container_registry(context Context, options Options) object => + base.container_registry(context, 'Standard', options) diff --git a/src/recommended/key-vault.bicep b/src/recommended/key-vault.bicep index 78a7676..4f0b4ab 100644 --- a/src/recommended/key-vault.bicep +++ b/src/recommended/key-vault.bicep @@ -1,9 +1,9 @@ -import * as common from '../base/key-vault.bicep' +import * as base from '../base/key-vault.bicep' import { Context, Options } from '../types.bicep' @description('Key vault') @export() -func keyVault(context Context, options Options) object => common.keyVault(context, 'standard', options) +func keyVault(context Context, options Options) object => base.keyVault(context, 'standard', options) // Sku @export() @@ -17,4 +17,4 @@ func withPremiumSku() object => { // Soft delete @export() -func withSoftDelete(context Context, enable bool) object => common.withSoftDelete(enable) +func withSoftDelete(context Context, enable bool) object => base.withSoftDelete(enable) diff --git a/src/recommended/log-analytics.bicep b/src/recommended/log-analytics.bicep new file mode 100644 index 0000000..b653bb5 --- /dev/null +++ b/src/recommended/log-analytics.bicep @@ -0,0 +1,5 @@ +import { Context, Options } from '../types.bicep' +import * as base from '../base/log-analytics.bicep' + +@export() +func log_analytics(context Context, options Options) object => base.log_analytics(context, 'PerGB2018', options) diff --git a/src/recommended/resource-group.bicep b/src/recommended/resource-group.bicep index 13e7bb2..4419f12 100644 --- a/src/recommended/resource-group.bicep +++ b/src/recommended/resource-group.bicep @@ -1,5 +1,5 @@ import { Context, Options } from '../types.bicep' -import * as common from '../base/resource-group.bicep' +import * as base from '../base/resource-group.bicep' @export() -func resourceGroup(context Context, options Options) object => common.resourceGroup(context, options) +func resource_group(context Context, options Options) object => base.resource_group(context, options) diff --git a/src/recommended/static-web-app.bicep b/src/recommended/static-web-app.bicep index a14a9ae..0b9fa4c 100644 --- a/src/recommended/static-web-app.bicep +++ b/src/recommended/static-web-app.bicep @@ -2,8 +2,8 @@ import { Context, Options } from '../types.bicep' @export() -func staticWebApp(context Context, options Options) object => - base.staticWebApp( +func static_web_app(context Context, options Options) object => + base.static_web_app( context, union( [ @@ -16,7 +16,7 @@ func staticWebApp(context Context, options Options) object => ) @export() -func withManagedIdentity() object => base.withIdentity('SystemAssigned') +func with_managed_identity() object => base.with_identity('SystemAssigned') @export() -func withDefaultSku() object => base.withSku('Standard') +func withDefaultSku() object => base.with_sku('Standard') diff --git a/src/recommended/storage-account.bicep b/src/recommended/storage-account.bicep index 18dc4dc..621d436 100644 --- a/src/recommended/storage-account.bicep +++ b/src/recommended/storage-account.bicep @@ -1,22 +1,22 @@ -import * as common from '../base/storage-account.bicep' +import * as base from '../base/storage-account.bicep' import { Context, Options } from '../types.bicep' @export() -func storageAccount(context Context, options Options) object => - common.storageAccount( +func storage_account(context Context, options Options) object => + base.storage_account( context, 'Standard_LRS', 'StorageV2', union( [ - withAccessTier('Hot') + with_access_tier('Hot') ], options ) ) @export() -func withPublicAccess() object => common.withAllowPublicAccess(true) +func withPublicAccess() object => base.with_allow_public_access(true) @export() -func withAccessTier(accessTier common.AccessTier) object => common.withAccessTier(accessTier) +func with_access_tier(accessTier base.AccessTier) object => base.with_access_tier(accessTier) diff --git a/src/types.bicep b/src/types.bicep index 44e81f0..ec76c1e 100644 --- a/src/types.bicep +++ b/src/types.bicep @@ -1,6 +1,7 @@ @export() type Context = { name: string + project: string nameConventionTemplate: string environment: string location: Location @@ -101,6 +102,7 @@ type ResourceType = | 'serviceBusTopicSubscription' | 'applicationInsights' | 'storageAccount' + | 'logAnalytics' @export() type Abbreviation = @@ -120,3 +122,4 @@ type Abbreviation = | 'sbts' | 'appi' | 'st' + | 'la'