Build the Infrastructure as Code Using Bicep¶
Module Duration
30 minutes
Note
If you're not interested in manually deploying the Bicep files or creating the container registry yourself, and prefer not to delve into the details of how they work, then you can skip this section and head directly to either Deploy Infrastructure Using Github Actions or Deploy Infrastructure Using Azure DevOps depending on your DevOps tool of choice.
To begin, we need to define the Bicep modules that will be required to generate the Infrastructure code. Our goal for this module is to have a freshly created resource group that encompasses all the necessary resources and configurations - such as connection strings, secrets, environment variables, and Dapr components - which we utilized to construct our solution. By the end, we will have a new resource group that includes the following resources.
Note
To simplify the execution of the module, we will assume that you have already created latest images of three services and pushed them to a container registry. This section below guides you through different options of getting images pushed to either Azure Container Registry (ACR) or GitHub Container Registry (GHCR).
1. Add the Needed Extension to VS Code¶
To proceed, you must install an extension called Bicep. This extension will simplify building Bicep files as it offers IntelliSense, Validation, listing all available resource types, etc..
2. Define an Azure Container Apps Environment¶
Add a new folder named bicep
on the root project directory, then add another folder named modules
. Add file as shown below:
What we've added in the Bicep file above
- The module takes multiple parameters, all of which are set to default values. This indicates that if no value is specified, the default value will be utilized.
- The
location
parameter defaults to the location of the container resource group. Bicep has a function calledresourceGroup()
, which can be used to retrieve the location. - The parameters
prefix
andsuffix
could be used if you want to add a prefix or suffix to the resource names. - The parameter
tag
is used to tag the created resources. Tags are key-value pairs that help you identify resources based on settings that are relevant to your organization and deployment. - The parameters
containerAppsEnvironmentName
,logAnalyticsWorkspaceName
, andapplicationInsightName
have default values of resource names using the helper function nameduniqueString
. This function performs a 64-bit hash of the provided strings to create a unique string. This function is helpful when you need to create a unique name for a resource. We are passing theresourceGroup().id
to this function to ensure that if we executed this module on two different resource groups, the generated string will be a global unique name. - This module will create two resources. It will start by creating a
logAnalyticsWorkspace
, then anapplicationInsights
resource. Notice how we are setting thelogAnalyticsWorkspace.id
as an Application InsightsWorkspaceResourceId
. - Lastly we are creating the
containerAppsEnvironment
. Notice how we are setting thedaprAIInstrumentationKey
by using the Application InsightsInstrumentationKey
and then settinglogAnalyticsConfiguration.customerId
andlogAnalyticsConfiguration.sharedKey
. - The output of this module are a is parameter named
applicationInsightsName
. This output is needed as an input for a subsequent module.
3. Define an Azure Key Vault Resource¶
Add file as shown below under the folder bicep\modules
:
What we've added in the Bicep file above
- This module will create the Azure Key Vault resource which will be used to store secrets.
- The output of this module is a single parameter named
keyVaultId
. This output is needed as an input for a subsequent module.
4. Define a Azure Service Bus Resource¶
Add file as shown below under the folder bicep\modules
:
What we've added in the Bicep file above
- This module will create the Azure Service resource, a topic, a subscription for the consumer, and an authorization rule with
Manage
permissions. - The output of this module will return three output parameters which will be used as an input for a subsequent module.
5. Define an Azure CosmosDb Resource¶
Add file as shown below under the folder bicep\modules
:
What we've added in the Bicep file above
- This module will create the Azure Cosmos DB account, a Cosmos DB database, and a Cosmos DB collection.
- The output of this module will return three output parameters which will be used as an input for a subsequent module.
6. Define an Azure Storage Resource¶
Add file as shown below under the folder bicep\modules
:
What we've added in the Bicep file above
- This module will create the Azure Storage account, a storage queue service, and a queue.
- The output of this module will be a single output parameter which will be used as an input for a subsequent module.
7. Define Dapr Components¶
Next we will define all dapr components used in the solution in a single bicep module. To accomplish this, add a new file under the folder bicep\modules
as shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
|
What we've added in the Bicep file above
- This module will be responsible for creating all dapr components used in the solution. It accepts various input parameters needed by the dapr components.
-
Notice how we are using the keyword
existing
to obtain a strongly typed reference to the pre-created resource
8. Create Secrets Into Azure Key Vault¶
This module will have the responsibility of generating the secrets and saving them in Azure Key Vault. Additionally, it will establish a role assignment for the backend processor service, specifically of type Azure Role Key Vault Secrets User
, which will allow the service to access the Key Vault and retrieve the secrets.
To achieve this, create a new directory called container-apps\secrets
within the modules
folder. Add new file as shown below under the folder bicep\modules\container-apps\secrets
:
9. Define the Frontend Service Azure Container App¶
We will now begin defining the modules that are necessary for producing the container apps, starting with the Frontend App. To initiate this process, add a new file under the folder bicep\modules\container-apps
as shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
|
What we've added in the Bicep file above
- Observe the usage of the
@secure
attribute on input parameters that contain confidential information or keys. This attribute may be applied to both string and object parameters that encompass secretive values. By implementing this attribute, Azure will abstain from presenting the parameter values within the deployment logs or on the terminal if you happen to be utilizing Azure CLI. - The output parameters of this module will provide the fully qualified domain name (FQDN) for the frontend container application.
10. Define the Backend Api Service Azure Container App¶
Add a new file under the folder bicep\modules\container-apps
as shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
|
What we've added in the Bicep file above
-
Notice how we are assigning the Cosmosdb account a read/write access using the
Cosmos DB Built-in Data Contributor
role to the Backend API system assigned identity, by using the code below: -
A similar technique was applied when assigning the Azure Service Bus Data Sender role to the Backend API, enabling it to publish messages to Azure Service Bus utilizing the Backend API system-assigned identity. This was accomplished utilizing the following code:
11. Define the Backend Processor Service Azure Container App¶
Add a new file under the folder bicep\modules\container-apps
as shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
|
What we've added in the Bicep file above
- Notice how we are assigning the role
Azure Service Bus Data Receiver
to the Backend Processor to be able to consume/read messages from Azure Service Bus Topic using Backend Processor system assigned identity, by using the code below: - Within this module, we've invoked the module defined in step 8 which is responsible to create the secrets in Azure Key Vault and assign the role
Azure Role Key Vault Secrets User
to the Backend Processor Service, by using the code below:
12. Define a Container Module For the Three Container Apps¶
This module will act as a container for the three Container Apps modules defined in the previous three steps. It is optional to create it, but it makes it easier when we invoke all the created modules as you will see in the next step.
Add a new file under the folder bicep\modules
as shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
|
13. Define the Main Module For the Solution¶
Finally, we must specify the Main Bicep module that will connect all other modules together. This file will be referenced by the AZ CLI command when producing all resources.
To achieve this, add a new file under the bicep
directory as shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
|
What we've added in the Bicep file above
-
When calling the module
dapr-components.bicep
we are setting the value of the arraydependsOn
to the Container Apps Environment. This is called explicit dependency which aids the Bicep interpreter in comprehending the relationships between components. In this instance, the Container Apps Environment must be provisioned before the Dapr Components to guarantee a successful deployment. -
When calling the module
container-apps.bicep
, some of the input params are expecting are referencing another resource, for example consider the input param namedcosmosDbName
and the value used iscosmosDb.outputs.cosmosDbName
. This means that the modulecosmos-db.bicep
should be created successfully before creating the container apps module, this called Implicit dependency.
Deploy the Infrastructure and Create the Components¶
Start by creating a new resource group which will contain all the resources to be created by the Bicep scripts.
Create a parameters file which will simplify the invocation of the main bicep file. To achieve this, right click on file main.bicep
and select Generate Parameter File.
This will result in creating a file named main.parameters.json
similar to the file below:
Example
Note
To use this file, you need to edit this generated file and provide values for the parameters. You can use the same values shown above in sample file.
You only need to replace parameter values between the angle brackets <>
with values related to your container registry and SendGrid. Values for container registry and container images can be derived by following
one of the three options in next step.
In case you followed along with the whole workshop and would like to use your own sourcecode, make sure to replace the port numbers (80) by the port numbers that were generated when you created your docker files in vs code for your three applications (e.g., 5225). Make sure that the port numbers match the numbers in the respective docker files. If port numbers aren't matching, your deployment will work without errors, but the portal will report issues with the apps and calling one of the apps will result in a session timeout.
Next, we will prepare container images for the three container apps and update the values in main.parameters.json
file. You can do so by any of the three options below:
-
Create an Azure Container Registry (ACR) inside the newly created Resource Group:
-
Build and push the images to ACR. Make sure you are at the root project directory when executing the following commands:
-
Update the
main.parameters.json
file with the container registry name and the container images names as shown below:
All the container image are available in a public image repository. If you do not wish to build the container images from code directly, you can import it directly into your private container instance as shown below.
-
Create an Azure Container Registry (ACR) inside the newly created Resource Group:
-
Import the images to your private ACR as shown below:
-
Update the
main.parameters.json
file with the container registry name and the container images names as shown below:
All the container image are available in a public image repository. If you do not wish to build the container images from code directly, you can use the pre-built images from the public repository as shown below.
The public images can be set directly in the main.parameters.json
file:
Start the deployment by calling az deployment group create
. To accomplish this, open the PowerShell console and use the content below.
The Azure CLI will take the Bicep module and start creating the deployment in the resource group.