Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example/dependencies #10

Closed
wants to merge 14 commits into from
1 change: 1 addition & 0 deletions .github/workflows/mega-linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ jobs:
DISABLE: COPYPASTE,SPELL # Uncomment to disable copy-paste and spell checks
DISABLE_LINTERS: YAML_V8R,YAML_YAMLLINT,YAML_PRETTIER,REPOSITORY_CHECKOV,POWERSHELL_POWERSHELL,ACTION_ACTIONLINT
REPOSITORY_KICS_DISABLE_ERRORS: true
REPOSITORY_GITLEAKS_PR_COMMITS_SCAN: true

# Upload MegaLinter artifacts
- name: Archive production artifacts
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ Where possible, examples will be complimented by a blog post giving a deeper div

[Awesome Azure Bicep list](https://github.com/ElYusubov/AWESOME-Azure-Bicep)

[Azure Bicep Cheat Sheet](https://github.com/johnlokerse/azure-bicep-cheat-sheet)

## 🖌️ Authors

- [Rios Engineer](https://www.github.com/riosengineer)
91 changes: 91 additions & 0 deletions bicep-examples/dependencies/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Resource dependencies in Azure Bicep 🦾

There's two types of dependencies `implicit` and `explicit` in Azure Bicep. Within the `main.bicep` file example you'll notice many implicit and some explicit dependencies that you can review as a real example of how these two play a role in your Azure Bicep deployments.

> [!TIP]
> If you're interested in this examples solution and what it does then there is more information in this template repo [here](https://github.com/riosengineer/bicep-quickstart-frontdoor-private-endpoint-appservice) with supporting documentation and architectural drawing.

## Implicit 🔗

With `implicit` dependencies we are referencing another Azure resource within the same deployment, which means we'll not need to declare an explicit dependency. There are two common ways this is accomplished. For example:

```javascript
module appInsights 'modules/appInsights/appinsights.bicep' = {
name: '${uniqueString(deployment().name, location)}-appInsights'
params: {
name: appInsightsName
location: location
workspaceResourceId: logAnalytics.outputs.id
kind: 'web'
applicationType: 'web'
}
}
```

Note the `logAnalytics.outputs.id` symbolic name defined is referencing a previous module for this resources properties. This is how an implicit dependency is created and ARM will deploy in resources in their dependent order.

```javascript
resource frontDoorOriginGroup 'Microsoft.Cdn/profiles/originGroups@2021-06-01' = {
name: frontDoorOriginGroupName
parent: frontDoorProfile
properties: {
loadBalancingSettings: {
sampleSize: 4
successfulSamplesRequired: 3
}
healthProbeSettings: {
probePath: '/'
probeRequestType: 'HEAD'
probeProtocol: 'Http'
probeIntervalInSeconds: 100
}
}
}
```

Lastly, notice the `parent:` property defined in this Azure Front Door resource block above, where it's defining the symbolic name from the Azure CDN profile object. This is also an implicit dependency created between the two objects.

## Explicit 🖇️

```javascript
resource frontDoorProfile 'Microsoft.Cdn/profiles@2021-06-01' = {
name: frontDoorProfileName
location: 'global'
sku: {
name: frontDoorSkuName
}
dependsOn: [
webApp
webAppPlan
]
}
```

For explicit dependencies, we can use the `dependsOn` property to describe explicitly which resources we want this deployment to depend on.

In the case above, I don't want my Front Door deployment to start before the App service and App Plan have been deployed first, as I need them to exist for my origin backend.

## Deployment 🚀

> [!WARNING]
> This example deploys Azure Front Door Premium SKU which is circa $300 for the month. Do not leave running if you don't want to incur charges. Make sure to delete as soon as possible after deployment and you'll likely see very minimal costs.

Define the parameters in the top of the file before deploying.

In VisualStudio Code open a terminal and run:

CLI

```bash
az login
az set --subscription 'your subscription name'
az deployment create --confirm-with-what-if -g 'your resource group name' -f .\main.bicep
```

or PowerShell

```powershell
Connect-AzAccount
Set-AzContext -Subsription "your subsription name"
New-AzResourceGroupDeployment -Confirm -ResourceGroup "your resource group name" -TemplateFile "main.bicep"
```
229 changes: 229 additions & 0 deletions bicep-examples/dependencies/main.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
targetScope = 'resourceGroup'

// Change the below params to suit your deployment needs
// Go to the modules to amend IP schema, app plan sku/app code stack etc.
@description('Azure UK South region.')
param location string = resourceGroup().location

@description('Web App resource group name.')
param rg_web_workload string = 'rg-webapp-prod'

@description('Workload / corp / core landing zone subid.')
param workloadsSubId string = '00000000-0000-0000-0000-000000000000'

@description('Log analytics workspace name.')
param alaName string = 'ala-workspace-name'

@description('App service application insights name.')
param appInsightsName string = 'appinsights-name'

@description('Azure app service name.')
param webAppName string = 'webapp-001'

@description('The name of the Front Door endpoint to create. This must be globally unique.')
param afdWebEndpoint string = 'afd-${uniqueString(resourceGroup().id)}'

@description('The name of the SKU to use when creating the Front Door profile.')
@allowed([
'Standard_AzureFrontDoor'
'Premium_AzureFrontDoor'
])
param frontDoorSkuName string = 'Premium_AzureFrontDoor'

var frontDoorProfileName = 'afdpremium-web'
var frontDoorOriginGroupName = 'webapp-origin-group'
var frontDoorOriginName = 'webapp-origin-group'
var frontDoorRouteName = 'webapp-route'

///////////////
// Resources //
///////////////

// Azure App Service components

// vNet for integration
module vnet 'br/public:network/virtual-network:1.1.3' = {
name: '${uniqueString(deployment().name, location)}-webVnet'
scope: resourceGroup(workloadsSubId, rg_web_workload)
params: {
name: 'webapp-vnet'
addressPrefixes: [
'10.1.0.0/21'
]
subnets: [
{
name: 'webapp-snet'
addressPrefix: '10.1.1.0/24'
delegations: [
{
name: 'Microsoft.Web.serverFarms'
properties: {
serviceName: 'Microsoft.Web/serverFarms'
}
}
]
}
]
}
}

// Log Analytics workspace
module logAnalytics 'br/public:storage/log-analytics-workspace:1.0.3' = {
name: '${uniqueString(deployment().name, location)}-ala'
scope: resourceGroup(rg_web_workload)
params: {
name: alaName
location: location
}
}

// Application Insight
module appInsights 'modules/appInsights/appinsights.bicep' = {
name: '${uniqueString(deployment().name, location)}-appInsights'
scope: resourceGroup(workloadsSubId, rg_web_workload)
params: {
name: appInsightsName
location: location
workspaceResourceId: logAnalytics.outputs.id
kind: 'web'
applicationType: 'web'
}
}

// Azure App Plan
module webAppPlan 'modules/webApp/appPlan.bicep' = {
name: '${uniqueString(deployment().name, location)}-appPlan'
scope: resourceGroup(workloadsSubId, rg_web_workload)
params: {
name: 'appPlan'
location: location
sku: {
name: 'S1'
}
kind: 'App'
}
}

// Web App resource
module webApp 'modules/webApp/webApp.bicep' = {
name: '${uniqueString(deployment().name, location)}-webApp'
scope: resourceGroup(workloadsSubId, rg_web_workload)
params: {
name: webAppName
location: location
kind: 'app'
serverFarmResourceId: webAppPlan.outputs.resourceId
httpsOnly: true
publicNetworkAccess: 'Disabled'
appInsightResourceId: appInsights.outputs.resourceId
virtualNetworkSubnetId: vnet.outputs.subnetResourceIds[0]
siteConfig: {
detailedErrorLoggingEnabled: true
httpLoggingEnabled: true
requestTracingEnabled: true
ftpsState: 'Disabled'
minTlsVersion: '1.2'
alwaysOn: true
}
appSettingsKeyValuePairs: {
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: appInsights.outputs.instrumentationKey
}
managedIdentities: {
systemAssigned: true
}
}
}


// Front Door resource
resource frontDoorProfile 'Microsoft.Cdn/profiles@2021-06-01' = {
name: frontDoorProfileName
location: 'global'
sku: {
name: frontDoorSkuName
}
dependsOn: [
webApp
webAppPlan
]
}

// Front Door endpoint(s)
resource frontDoorEndpoint 'Microsoft.Cdn/profiles/afdEndpoints@2021-06-01' = {
name: afdWebEndpoint
parent: frontDoorProfile
location: 'global'
properties: {
enabledState: 'Enabled'
}
}

// Front Door origin group
resource frontDoorOriginGroup 'Microsoft.Cdn/profiles/originGroups@2021-06-01' = {
name: frontDoorOriginGroupName
parent: frontDoorProfile
properties: {
loadBalancingSettings: {
sampleSize: 4
successfulSamplesRequired: 3
}
healthProbeSettings: {
probePath: '/'
probeRequestType: 'HEAD'
probeProtocol: 'Http'
probeIntervalInSeconds: 100
}
}
}

// Front Door backend - Azure Web App
resource frontDoorOrigin 'Microsoft.Cdn/profiles/originGroups/origins@2022-11-01-preview' = {
name: frontDoorOriginName
parent: frontDoorOriginGroup
properties: {
hostName: webApp.outputs.defaultHostname
httpPort: 80
httpsPort: 443
originHostHeader: webApp.outputs.defaultHostname
priority: 1
weight: 1000
sharedPrivateLinkResource: {
groupId: 'sites'
privateLink: {
id: webApp.outputs.resourceId
}
privateLinkLocation: location
requestMessage: 'AFD PE to Web App'
status: 'Pending'
}
}
}

// Front Door route
resource frontDoorRoute 'Microsoft.Cdn/profiles/afdEndpoints/routes@2021-06-01' = {
name: frontDoorRouteName
parent: frontDoorEndpoint
dependsOn: [
frontDoorOrigin // This explicit dependency is required to ensure that the origin group is not empty when the route is created.
]
properties: {
originGroup: {
id: frontDoorOriginGroup.id
}
supportedProtocols: [
'Http'
'Https'
]
patternsToMatch: [
'/*'
]
forwardingProtocol: 'HttpsOnly'
linkToDefaultDomain: 'Enabled'
httpsRedirect: 'Enabled'
}
}

// Output FQDNs
output appServiceHostName string = webApp.outputs.defaultHostname
output frontDoorEndpointHostName string = frontDoorEndpoint.properties.hostName
Loading