|
| 1 | +--- |
| 2 | +title: Create connections with IaC tools |
| 3 | +description: Learn how to translate your infrastructure to an IaC template |
| 4 | +author: houk-ms |
| 5 | +ms.service: service-connector |
| 6 | +ms.topic: how-to |
| 7 | +ms.date: 10/20/2023 |
| 8 | +ms.author: honc |
| 9 | +--- |
| 10 | +# How to translate your infrastructure to an IaC template |
| 11 | + |
| 12 | +Service Connector helps users connect their compute services to target backing services in just a few clicks or commands. When moving from a getting-started to a production stage, users also need to make the transition from using manual configurations to using Infrastructure as Code (IaC) templates in their CI/CD pipelines. In this guide, we show how to translate your connected Azure services to IaC templates. |
| 13 | + |
| 14 | +## Prerequisites |
| 15 | + |
| 16 | +- This guide assumes that you're aware of the [Service Connector IaC limitations](./known-limitations.md). |
| 17 | + |
| 18 | +## Solution overview |
| 19 | + |
| 20 | +Translating the infrastructure to IaC templates usually involves two major parts: the logics to provision source and target services, and the logics to build connections. To implement the logics to provision source and target services, there are two options: |
| 21 | + |
| 22 | +* Authoring the template from scratch. |
| 23 | +* Exporting the template from Azure and polish it. |
| 24 | + |
| 25 | +To implement the logics to build connections, there are also two options: |
| 26 | + |
| 27 | +* Using Service Connector in the template. |
| 28 | +* Using template logics to configure source and target services directly. |
| 29 | + |
| 30 | +Combinations of these different options can produce different solutions. Due to [IaC limitations](./known-limitations.md) in Service Connector, we recommend that you implement the following solutions in the order presented below. To apply these solutions, you must understand the IaC tools and the template authoring grammar. |
| 31 | + |
| 32 | +| Solution | Provision source and target | Build connection | Applicable scenario | Pros | Cons | |
| 33 | +| :------: | :-------------------------: | :-------------------------------------------------------: | :----------------------------------------------------------------------: | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | |
| 34 | +| 1 | Authoring from scratch | Use Service Connector | Has liveness check on the cloud resources before allowing live traffics | - Template is simple and readable<br />- Service Connector brings extra values | - Cost to check cloud resources liveness | |
| 35 | +| 2 | Authoring from scratch | Configure source and target services directly in template | No liveness check on the cloud resources | - Template is simple and readable | - Service Connector features aren't available | |
| 36 | +| 3 | Export and polish | Use Service Connector | Has liveness check on the cloud resources before allowing live traffics | - Resources are exactly the same as in the cloud<br />- Service Connector brings extra values | - Cost to check cloud resources liveness<br />- Supports only ARM templates<br />- Efforts required to understand and polish the template | |
| 37 | +| 4 | Export and polish | Configure source and target services directly in template | No liveness check on the cloud resources | - Resources are exactly same as on the cloud | - Support only ARM template<br />- Efforts to understand and polish the template<br />- Service Connector features aren't available | |
| 38 | + |
| 39 | +## Authoring templates |
| 40 | + |
| 41 | +The following sections show how to create a web app and a storage account and connect them with a system-assigned identity using Bicep. It shows how to do this both using Service Connector and using template logics. |
| 42 | + |
| 43 | +### Provision source and target services |
| 44 | + |
| 45 | +**Authoring from scratch** |
| 46 | + |
| 47 | +Authoring the template from scratch is the preferred and recommended way to provision source and target services, as it's easy to get started and makes the template simple and readable. Following is an example, using a minimal set of parameters to create a webapp and a storage account. |
| 48 | + |
| 49 | +```bicep |
| 50 | +// This template creates a webapp and a storage account. |
| 51 | +// In order to make it more readable, we use only the mininal set of parameters to create the resources. |
| 52 | +
|
| 53 | +param location string = resourceGroup().location |
| 54 | +// App Service plan parameters |
| 55 | +param planName string = 'plan_${uniqueString(resourceGroup().id)}' |
| 56 | +param kind string = 'linux' |
| 57 | +param reserved bool = true |
| 58 | +param sku string = 'B1' |
| 59 | +// Webapp parameters |
| 60 | +param webAppName string = 'webapp-${uniqueString(resourceGroup().id)}' |
| 61 | +param linuxFxVersion string = 'PYTHON|3.8' |
| 62 | +param identityType string = 'SystemAssigned' |
| 63 | +param appSettings array = [] |
| 64 | +// Storage account parameters |
| 65 | +param storageAccountName string = 'account${uniqueString(resourceGroup().id)}' |
| 66 | +
|
| 67 | +
|
| 68 | +// Create an app service plan |
| 69 | +resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = { |
| 70 | + name: planName |
| 71 | + location: location |
| 72 | + kind: kind |
| 73 | + sku: { |
| 74 | + name: sku |
| 75 | + } |
| 76 | + properties: { |
| 77 | + reserved: reserved |
| 78 | + } |
| 79 | +} |
| 80 | +
|
| 81 | +
|
| 82 | +// Create a web app |
| 83 | +resource appService 'Microsoft.Web/sites@2022-09-01' = { |
| 84 | + name: webAppName |
| 85 | + location: location |
| 86 | + properties: { |
| 87 | + serverFarmId: appServicePlan.id |
| 88 | + siteConfig: { |
| 89 | + linuxFxVersion: linuxFxVersion |
| 90 | + appSettings: appSettings |
| 91 | + } |
| 92 | + } |
| 93 | + identity: { |
| 94 | + type: identityType |
| 95 | + } |
| 96 | +} |
| 97 | +
|
| 98 | +
|
| 99 | +// Create a storage account |
| 100 | +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { |
| 101 | + name: storageAccountName |
| 102 | + location: location |
| 103 | + sku: { |
| 104 | + name: 'Standard_LRS' |
| 105 | + } |
| 106 | + kind: 'StorageV2' |
| 107 | +} |
| 108 | +``` |
| 109 | + |
| 110 | +**Export and polish** |
| 111 | + |
| 112 | +If the resources you're provisioning are exactly the same ones as the ones you have in the cloud, exporting the template from Azure might be another option. The two premises of this approach are: the resources exist in Azure and you're using ARM templates for your IaC. The `Export template` button is usually at the bottom of the sidebar on Azure portal. The exported ARM template reflects the resource's current states, including the settings configured by Service Connector. You usually need to know about the resource properties to polish the exported template. |
| 113 | + |
| 114 | +:::image type="content" source="./media/how-to/export-webapp-template.png" alt-text="Screenshot of the Azure portal, exporting arm template of a web app."::: |
| 115 | + |
| 116 | +### Build connection logics |
| 117 | + |
| 118 | +**Using Service Connector** |
| 119 | + |
| 120 | +Creating connections between the source and target service using Service Connector is the preferred and recommended way if the [Service Connector ](./known-limitations.md)[IaC limitation](./known-limitations.md) doesn't matter for your scenario. Service Connector makes the template simpler and also provides additional elements, such as the connection health validation, which you won't have if you're building connections through template logics directly. |
| 121 | + |
| 122 | +```bicep |
| 123 | +// The template builds a connection between a webapp and a storage account |
| 124 | +// with a system-assigned identity using Service Connector |
| 125 | +
|
| 126 | +param webAppName string = 'webapp-${uniqueString(resourceGroup().id)}' |
| 127 | +param storageAccountName string = 'account${uniqueString(resourceGroup().id)}' |
| 128 | +param connectorName string = 'connector_${uniqueString(resourceGroup().id)}' |
| 129 | +
|
| 130 | +// Get an existing webapp |
| 131 | +resource webApp 'Microsoft.Web/sites@2022-09-01' existing = { |
| 132 | + name: webAppName |
| 133 | +} |
| 134 | +
|
| 135 | +// Get an existig storage |
| 136 | +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' existing = { |
| 137 | + name: storageAccountName |
| 138 | +} |
| 139 | +
|
| 140 | +// Create a Service Connector resource for the webapp |
| 141 | +// to connect to a storage account using system identity |
| 142 | +resource serviceConnector 'Microsoft.ServiceLinker/linkers@2022-05-01' = { |
| 143 | + name: connectorName |
| 144 | + scope: webApp |
| 145 | + properties: { |
| 146 | + clientType: 'python' |
| 147 | + targetService: { |
| 148 | + type: 'AzureResource' |
| 149 | + id: storageAccount.id |
| 150 | + } |
| 151 | + authInfo: { |
| 152 | + authType: 'systemAssignedIdentity' |
| 153 | + } |
| 154 | + } |
| 155 | +} |
| 156 | +``` |
| 157 | + |
| 158 | +For the formats of properties and values needed when creating a Service Connector resource, check [how to provide correct parameters](./how-to-provide-correct-parameters.md). You can also preview and download an ARM template for reference when creating a Service Connector resource in the Azure portal. |
| 159 | + |
| 160 | +:::image type="content" source="./media/how-to/export-sc-template.png" alt-text="Screenshot of the Azure portal, exporting arm template of a service connector resource."::: |
| 161 | + |
| 162 | +**Using template logics** |
| 163 | + |
| 164 | +For the scenarios where the Service Connector [IaC limitation](./known-limitations.md) matters, consider building connections using the template logics directly. The following template is an example showing how to connect a storage account to a web app using a system-assigned identity. |
| 165 | + |
| 166 | +```bicep |
| 167 | +// The template builds a connection between a webapp and a storage account |
| 168 | +// with a system-assigned identity without using Service Connector |
| 169 | +
|
| 170 | +param webAppName string = 'webapp-${uniqueString(resourceGroup().id)}' |
| 171 | +param storageAccountName string = 'account${uniqueString(resourceGroup().id)}' |
| 172 | +param storageBlobDataContributorRole string = 'ba92f5b4-2d11-453d-a403-e96b0029c9fe' |
| 173 | +
|
| 174 | +// Get an existing webapp |
| 175 | +resource webApp 'Microsoft.Web/sites@2022-09-01' existing = { |
| 176 | + name: webAppName |
| 177 | +} |
| 178 | +
|
| 179 | +// Get an existing storage account |
| 180 | +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' existing = { |
| 181 | + name: storageAccountName |
| 182 | +} |
| 183 | +
|
| 184 | +// Operation: Enable system-assigned identity on the source service |
| 185 | +// No action needed as this is enabled when creating the webapp |
| 186 | +
|
| 187 | +// Operation: Configure the target service's endpoint on the source service's app settings |
| 188 | +resource appSettings 'Microsoft.Web/sites/config@2022-09-01' = { |
| 189 | + name: 'appsettings' |
| 190 | + parent: webApp |
| 191 | + properties: { |
| 192 | + AZURE_STORAGEBLOB_RESOURCEENDPOINT: storageAccount.properties.primaryEndpoints.blob |
| 193 | + } |
| 194 | +} |
| 195 | +
|
| 196 | +// Operation: Configure firewall on the target service to allow the source service's outbound IPs |
| 197 | +// No action needed as storage account allows all IPs by default |
| 198 | +
|
| 199 | +// Operation: Create role assignment for the source service's identity on the target service |
| 200 | +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { |
| 201 | + scope: storageAccount |
| 202 | + name: guid(resourceGroup().id, storageBlobDataContributorRole) |
| 203 | + properties: { |
| 204 | + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', storageBlobDataContributorRole) |
| 205 | + principalId: webApp.identity.principalId |
| 206 | + } |
| 207 | +} |
| 208 | +``` |
| 209 | + |
| 210 | +When building connections using template logics directly, it's crucial to understand what Service Connector does for each kind of authentication type, as the template logics are equivalent to the Service Connector backend operations. The following table shows the operation details that you need translate to template logics for each kind of authentication type. |
| 211 | + |
| 212 | +| Auth type | Service Connector operations | |
| 213 | +| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
| 214 | +| Secret / Connection string | - Configure the target service's connection string on the source service's app settings<br />- Configure firewall on the target service to allow the source service's outbound IPs | |
| 215 | +| System-assigned managed identity | - Configure the target service's endpoint on the source service's app settings<br />- Configure firewall on the target service to allow the source service's outbound IPs<br />- Enable system assigned identity on the source service<br />- Create role assignment for the source service's identity on the target service | |
| 216 | +| User-assigned managed identity | - Configure the target service's endpoint on the source service's app settings<br />- Configure firewall on the target service to allow the source service's outbound IPs<br />- Bind user assigned identity to the source service<br />- Create role assignment for the user assigned identity on the target service | |
| 217 | +| Service principal | - Configure the target service's endpoint on the source service's app settings<br />- Configure the service principal's appId and secret on the source service's app settings<br />- Configure firewall on the target service to allow the source service's outbound IPs<br />- Create role assignment for the service principal on the target service | |
0 commit comments