From 8ff2ddf1f79a77b5212bde163b85f478282e7e12 Mon Sep 17 00:00:00 2001 From: Kevin BEAUGRAND Date: Wed, 16 Nov 2022 08:34:00 +0100 Subject: [PATCH 1/2] Fix #1186 - Use bicep to develop, build arm templates --- .github/workflows/arm-ttk.yml | 20 +- src/AzureIoTHub.Portal.sln | 7 +- templates/azuredeploy.bicep | 132 ++++++++++ templates/azuredeploy.json | 285 -------------------- templates/portalDeployWithLoRa.bicep | 240 +++++++++++++++++ templates/portalDeployWithLoRa.json | 301 --------------------- templates/portalDeployWithoutLoRa.bicep | 267 +++++++++++++++++++ templates/portalDeployWithoutLoRa.json | 333 ------------------------ 8 files changed, 659 insertions(+), 926 deletions(-) create mode 100644 templates/azuredeploy.bicep delete mode 100644 templates/azuredeploy.json create mode 100644 templates/portalDeployWithLoRa.bicep delete mode 100644 templates/portalDeployWithLoRa.json create mode 100644 templates/portalDeployWithoutLoRa.bicep delete mode 100644 templates/portalDeployWithoutLoRa.json diff --git a/.github/workflows/arm-ttk.yml b/.github/workflows/arm-ttk.yml index 0bb4dbd43..2f0a76f11 100644 --- a/.github/workflows/arm-ttk.yml +++ b/.github/workflows/arm-ttk.yml @@ -1,8 +1,8 @@ -name: Validate ARM templates +name: Validate Bicep templates on: pull_request: - branches: [ main, v3-dev ] + branches: [ main ] paths: - 'templates/**' push: @@ -24,6 +24,7 @@ on: jobs: validate_arm_templates: + name: Build Bicep and Validate ARM Templates runs-on: ubuntu-latest steps: @@ -45,9 +46,20 @@ jobs: with: repository: Azure/arm-ttk path: arm-ttk - + + - name: Checkout Azure/iotedge-lorawan-starterkit + uses: actions/checkout@v3.1.0 + with: + repository: Azure/iotedge-lorawan-starterkit + ref: dev + path: iotedge-lorawan-starterkit + + - name: Generate ARM file + working-directory: templates + run: az bicep build --file azuredeploy.bicep --outfile azuredeploy.json + - name: Run arm-ttk shell: pwsh run: | Import-Module ./arm-ttk/arm-ttk/arm-ttk.psd1 - Test-AzTemplate -TemplatePath ./templates + Test-AzTemplate -TemplatePath ./templates/azuredeploy.json -Skip 'Template Should Not Contain Blanks','URIs Should Be Properly Constructed' \ No newline at end of file diff --git a/src/AzureIoTHub.Portal.sln b/src/AzureIoTHub.Portal.sln index 5702b516b..4459186ea 100644 --- a/src/AzureIoTHub.Portal.sln +++ b/src/AzureIoTHub.Portal.sln @@ -25,6 +25,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{3CA1 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{9EF190B3-41E2-48E1-BA4E-E129589DD250}" ProjectSection(SolutionItems) = preProject + ..\.github\workflows\arm-ttk.yml = ..\.github\workflows\arm-ttk.yml ..\.github\workflows\awesome-ideas.yml = ..\.github\workflows\awesome-ideas.yml ..\.github\workflows\build.yml = ..\.github\workflows\build.yml ..\.github\workflows\codeql.yml = ..\.github\workflows\codeql.yml @@ -35,10 +36,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "templates", "templates", "{3F7A2982-4F0B-45F9-9FCA-923D5A7A1511}" ProjectSection(SolutionItems) = preProject - ..\templates\azuredeploy.json = ..\templates\azuredeploy.json + ..\templates\azuredeploy.bicep = ..\templates\azuredeploy.bicep ..\templates\azuredeployUI.json = ..\templates\azuredeployUI.json - ..\templates\portalDeployWithLoRa.json = ..\templates\portalDeployWithLoRa.json - ..\templates\portalDeployWithoutLoRa.json = ..\templates\portalDeployWithoutLoRa.json + ..\templates\portalDeployWithLoRa.bicep = ..\templates\portalDeployWithLoRa.bicep + ..\templates\portalDeployWithoutLoRa.bicep = ..\templates\portalDeployWithoutLoRa.bicep EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ISSUE_TEMPLATES", "ISSUE_TEMPLATES", "{4B89F192-EA33-4121-9809-D99621C40F3B}" diff --git a/templates/azuredeploy.bicep b/templates/azuredeploy.bicep new file mode 100644 index 000000000..d56cc8806 --- /dev/null +++ b/templates/azuredeploy.bicep @@ -0,0 +1,132 @@ +@description('The Azure resources location') +param location string = resourceGroup().location + +@description('Prefix used for resource names. Should be unique as this will also be used for domain names.') +param uniqueSolutionPrefix string + +@description('PostgreSQL user') +param pgsqlAdminLogin string + +@description('PostgreSQL password') +@secure() +param pgsqlAdminPassword string + +@description('The Open ID Authority') +param openIdAuthority string + +@description('The Open ID metadata Url from the Identity provider') +param openIdMetadataURL string + +@description('The client ID for the B2C tenant') +param clientId string + +@description('The API client ID for the B2C tenant') +param apiClientId string + +@description('The name of the Edge gateway') +param edgeGatewayName string = 'TestLoRaWANGateway' + +@description('Provision a final LoRa device in the IoT hub in addition to the gateway') +param deployDevice bool = true + +@description('Provide the reset pin value of your gateway. Please refer to the doc if you are unfamiliar with the value') +param resetPin int = 2 + +@description('In what region is your gateway deployed?') +@allowed([ + 'AS923-1' + 'AS923-2' + 'AS923-3' + 'AU915' + 'CN470RP1' + 'CN470RP2' + 'EU863' + 'US902' +]) +param region string = 'EU863' + +@description('[In Mbps] Custom SPI speed for your gateway, currently only supported for ARM gateways') +@allowed([ + 8 + 2 +]) +param spiSpeed int = 8 + +@description('SPI Dev version for x86 based gateway') +@allowed([ + 1 + 2 +]) +param spiDev int = 2 + +@description('Enable LoRaWAN feature?') +param isLoRaFeatureEnabled bool = true + +@description('To enable Awesome-Ideas feature when set to true') +param ideasEnabled bool = false + +@description('Url of Awesome-Ideas, to publish ideas submitted by users. Required when ideasEnabled is true') +param ideasUrl string = '' + +@description('Authentication header to interact with Awesome-Ideas. Required when ideasEnabled is true') +param ideasAuthenticationHeader string = 'Ocp-Apim-Subscription-Key' + +@description('Authentication token to interact with Awesome-Ideas. Required when ideasEnabled is true') +param ideasAuthenticationToken string = '' + +var starterKitDeploymentName = 'lorawan-starter-kit' +var portalWithLoRaDeploymentName = 'iothub-portal-with-lora' +var portalWithoutLoRaDeploymentName = 'iothub-portal-without-lora' + +module starterKitDeployment '../iotedge-lorawan-starterkit/TemplateBicep/main.bicep' = if (isLoRaFeatureEnabled) { + name: starterKitDeploymentName + params: { + location: location + uniqueSolutionPrefix: uniqueSolutionPrefix + edgeGatewayName: edgeGatewayName + deployDevice: deployDevice + resetPin: resetPin + region: region + spiSpeed: spiSpeed + spiDev: spiDev + } +} + +module portalWithLoRaDeployment './portalDeployWithLoRa.bicep' = if (isLoRaFeatureEnabled) { + name: portalWithLoRaDeploymentName + params: { + location: location + uniqueSolutionPrefix: uniqueSolutionPrefix + pgsqlAdminLogin: pgsqlAdminLogin + pgsqlAdminPassword: pgsqlAdminPassword + openIdAuthority: openIdAuthority + openIdMetadataURL: openIdMetadataURL + apiClientId: apiClientId + clientId: clientId + ideasEnabled: ideasEnabled + ideasUrl: ideasUrl + ideasAuthenticationHeader: ideasAuthenticationHeader + ideasAuthenticationToken: ideasAuthenticationToken + } + dependsOn: [ + starterKitDeployment + ] +} + +module portalWithoutLoRaDeployment './portalDeployWithoutLoRa.bicep' = if (!isLoRaFeatureEnabled) { + name: portalWithoutLoRaDeploymentName + params: { + location: location + uniqueSolutionPrefix: uniqueSolutionPrefix + pgsqlAdminLogin: pgsqlAdminLogin + pgsqlAdminPassword: pgsqlAdminPassword + openIdAuthority: openIdAuthority + openIdMetadataURL: openIdMetadataURL + apiClientId: apiClientId + clientId: clientId + ideasEnabled: ideasEnabled + ideasUrl: ideasUrl + ideasAuthenticationHeader: ideasAuthenticationHeader + ideasAuthenticationToken: ideasAuthenticationToken + } +} \ No newline at end of file diff --git a/templates/azuredeploy.json b/templates/azuredeploy.json deleted file mode 100644 index c312c27fb..000000000 --- a/templates/azuredeploy.json +++ /dev/null @@ -1,285 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "uniqueSolutionPrefix": { - "type": "string", - "metadata": { - "description": "Prefix used for resource names. Should be unique as this will also be used for domain names." - } - }, - "pgsqlAdminLogin": { - "type": "string", - "metadata": { - "description": "PostgreSQL user" - } - }, - "pgsqlAdminPassword": { - "type": "securestring", - "metadata": { - "description": "PostgreSQL password" - } - }, - "openIdAuthority": { - "type": "string", - "metadata": { - "description": "The Open ID Authority" - } - }, - "openIdMetadataURL": { - "type": "string", - "metadata": { - "description": "The Open ID metadata Url from the Identity provider" - } - }, - "clientId": { - "type": "string", - "metadata": { - "description": "The client ID for the B2C tenant" - } - }, - "apiClientId": { - "type": "string", - "metadata": { - "description": "The API client ID for the B2C tenant" - } - }, - "edgeGatewayName": { - "type": "string", - "defaultValue": "TestLoRaWANGateway", - "metadata": { - "description": "The name of the Edge gateway" - } - }, - "deployDevice": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Provision a final LoRa device in the IoT hub in addition to the gateway" - } - }, - "resetPin": { - "type": "int", - "defaultValue": 2, - "metadata": { - "description": "Provide the reset pin value of your gateway. Please refer to the doc if you are unfamiliar with the value" - } - }, - "region": { - "type": "string", - "allowedValues": [ - "EU", - "US" - ], - "defaultValue": "EU", - "metadata": { - "description": "In what region is your gateway deployed?" - } - }, - "spiSpeed": { - "type": "int", - "allowedValues": [ - 8, - 2 - ], - "defaultValue": 8, - "metadata": { - "description": "[In Mbps] Custom SPI speed for your gateway, currently only supported for ARM gateways" - } - }, - "spiDev": { - "type": "int", - "allowedValues": [ - 1, - 2 - ], - "defaultValue": 2, - "metadata": { - "description": "SPI Dev version for x86 based gateway" - } - }, - "isLoRaFeatureEnabled": { - "defaultValue": true, - "type": "bool", - "metadata": { - "description": "Enable LoRaWAN feature?" - } - }, - "ideasEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "To enable Awesome-Ideas feature when set to true" - } - }, - "ideasUrl": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Url of Awesome-Ideas, to publish ideas submitted by users. Required when ideasEnabled is true" - } - }, - "ideasAuthenticationHeader": { - "type": "string", - "defaultValue": "Ocp-Apim-Subscription-Key", - "metadata": { - "description": "Authentication header to interact with Awesome-Ideas. Required when ideasEnabled is true" - } - }, - "ideasAuthenticationToken": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Authentication token to interact with Awesome-Ideas. Required when ideasEnabled is true" - } - } - }, - "variables": { - "startKitDeploymentName": "lorawan-starter-kit", - "portalWithLoRaDeploymentName": "iothub-portal-with-lora", - "portalWithoutLoRaDeploymentName": "iothub-portal-without-lora" - }, - "resources": [ - { - "condition": "[parameters('isLoRaFeatureEnabled')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2021-04-01", - "name": "[variables('startKitDeploymentName')]", - "properties": { - "mode": "Incremental", - "templateLink": { - "uri": "https://raw.githubusercontent.com/Azure/iotedge-lorawan-starterkit/master/Template/azuredeploy.json", - "contentVersion": "1.0.0.0" - }, - "parameters": { - "uniqueSolutionPrefix": { - "value": "[parameters('uniqueSolutionPrefix')]" - }, - "edgeGatewayName": { - "value": "[parameters('edgeGatewayName')]" - }, - "deployDevice": { - "value": "[parameters('deployDevice')]" - }, - "resetPin": { - "value": "[parameters('resetPin')]" - }, - "region": { - "value": "[parameters('region')]" - }, - "spiSpeed": { - "value": "[parameters('spiSpeed')]" - }, - "spiDev": { - "value": "[parameters('spiDev')]" - } - } - } - }, - { - "condition": "[parameters('isLoRaFeatureEnabled')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2021-04-01", - "name": "[variables('portalWithLoRaDeploymentName')]", - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', variables('startKitDeploymentName'))]" - ], - "properties": { - "mode": "Incremental", - "templateLink": { - "uri": "https://raw.githubusercontent.com/CGI-FR/IoT-Hub-Portal/main/templates/portalDeployWithLoRa.json", - "contentVersion": "1.0.0.0" - }, - "parameters": { - "location": { - "value": "[resourceGroup().location]" - }, - "uniqueSolutionPrefix": { - "value": "[parameters('uniqueSolutionPrefix')]" - }, - "pgsqlAdminLogin": { - "value": "[parameters('pgsqlAdminLogin')]" - }, - "pgsqlAdminPassword": { - "value": "[parameters('pgsqlAdminPassword')]" - }, - "openIdAuthority": { - "value": "[parameters('openIdAuthority')]" - }, - "openIdMetadataURL": { - "value": "[parameters('openIdMetadataURL')]" - }, - "apiClientId": { - "value": "[parameters('apiClientId')]" - }, - "clientId": { - "value": "[parameters('clientId')]" - }, - "ideasEnabled": { - "value": "[parameters('ideasEnabled')]" - }, - "ideasUrl": { - "value": "[parameters('ideasUrl')]" - }, - "ideasAuthenticationHeader": { - "value": "[parameters('ideasAuthenticationHeader')]" - }, - "ideasAuthenticationToken": { - "value": "[parameters('ideasAuthenticationToken')]" - } - } - } - }, - { - "condition": "[not(parameters('isLoRaFeatureEnabled'))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2021-04-01", - "name": "[variables('portalWithoutLoRaDeploymentName')]", - "properties": { - "mode": "Incremental", - "templateLink": { - "uri": "https://raw.githubusercontent.com/CGI-FR/IoT-Hub-Portal/main/templates/portalDeployWithoutLoRa.json", - "contentVersion": "1.0.0.0" - }, - "parameters": { - "location": { - "value": "[resourceGroup().location]" - }, - "uniqueSolutionPrefix": { - "value": "[parameters('uniqueSolutionPrefix')]" - }, - "pgsqlAdminLogin": { - "value": "[parameters('pgsqlAdminLogin')]" - }, - "pgsqlAdminPassword": { - "value": "[parameters('pgsqlAdminPassword')]" - }, - "openIdAuthority": { - "value": "[parameters('openIdAuthority')]" - }, - "openIdMetadataURL": { - "value": "[parameters('openIdMetadataURL')]" - }, - "apiClientId": { - "value": "[parameters('apiClientId')]" - }, - "clientId": { - "value": "[parameters('clientId')]" - }, - "ideasEnabled": { - "value": "[parameters('ideasEnabled')]" - }, - "ideasUrl": { - "value": "[parameters('ideasUrl')]" - }, - "ideasAuthenticationHeader": { - "value": "[parameters('ideasAuthenticationHeader')]" - }, - "ideasAuthenticationToken": { - "value": "[parameters('ideasAuthenticationToken')]" - } - } - } - } - ] -} \ No newline at end of file diff --git a/templates/portalDeployWithLoRa.bicep b/templates/portalDeployWithLoRa.bicep new file mode 100644 index 000000000..537396486 --- /dev/null +++ b/templates/portalDeployWithLoRa.bicep @@ -0,0 +1,240 @@ +@description('Location for the resources.') +param location string + +@description('Prefix used for resource names. Should be unique as this will also be used for domain names.') +param uniqueSolutionPrefix string + +@description('PostgreSQL user') +param pgsqlAdminLogin string = concat(uniqueString(resourceGroup().id, newGuid())) + +@description('PostgreSQL password') +@secure() +param pgsqlAdminPassword string = '${uniqueString(resourceGroup().id, newGuid())}x!' + +@description('The Open ID Authority') +param openIdAuthority string + +@description('The Open ID metadata Url from the Identity provider') +param openIdMetadataURL string + +@description('The client ID for the B2C tenant') +param clientId string + +@description('The API client ID for the B2C tenant') +param apiClientId string + +@description('To enable Awesome-Ideas feature when set to true') +param ideasEnabled bool = false + +@description('Url of Awesome-Ideas, to publish ideas submitted by users. Required when ideasEnabled is true') +param ideasUrl string = '' + +@description('Authentication header to interact with Awesome-Ideas. Required when ideasEnabled is true') +param ideasAuthenticationHeader string = 'Ocp-Apim-Subscription-Key' + +@description('Authentication token to interact with Awesome-Ideas. Required when ideasEnabled is true') +param ideasAuthenticationToken string = '' + +var pgsqlServerName = '${uniqueSolutionPrefix}pgsql' +var iotHubName = '${uniqueSolutionPrefix}hub' +var dpsName = '${uniqueSolutionPrefix}dps' +var siteName = '${uniqueSolutionPrefix}portal' +var servicePlanName = '${uniqueSolutionPrefix}asp' +var functionAppName = '${uniqueSolutionPrefix}function' +var storageAccountName = '${uniqueSolutionPrefix}storage' +var iotHubOwnerPolicyName = 'iothubowner' +var provisioningserviceownerPolicyName = 'provisioningserviceowner' +var deviceImageContainerName = 'device-images' +var iamScopeName = 'API.Access' +var storageAccountId = '${resourceGroup().id}/providers/Microsoft.Storage/storageAccounts/${storageAccountName}' +var appInsightName = '${uniqueSolutionPrefix}insight' +var functionAppDefaultHost = '${resourceId('Microsoft.Web/sites', functionAppName)}/host/default/' + +resource storageAccountName_default_deviceImageContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2022-05-01' = { + name: '${storageAccountName}/default/${deviceImageContainerName}' +} + +resource dps 'Microsoft.Devices/provisioningServices@2021-10-15' = { + name: dpsName + location: location + sku: { + name: 'S1' + capacity: 1 + } + properties: { + state: 'Active' + iotHubs: [ + { + connectionString: 'HostName=${iotHubName}.azure-devices.net;SharedAccessKeyName=${iotHubOwnerPolicyName};SharedAccessKey=${listKeys(resourceId('Microsoft.Devices/IotHubs/IotHubKeys', iotHubName, iotHubOwnerPolicyName), '2021-07-02').primaryKey}' + location: location + } + ] + allocationPolicy: 'Hashed' + } +} + +resource pgsqlServer 'Microsoft.DBforPostgreSQL/servers@2017-12-01' = { + name: pgsqlServerName + location: location + sku: { + name: 'B_Gen5_2' + tier: 'Basic' + capacity: 2 + size: '5120' + family: 'Gen5' + } + properties: { + createMode: 'Default' + version: '11' + administratorLogin: pgsqlAdminLogin + administratorLoginPassword: pgsqlAdminPassword + storageProfile: { + storageMB: 5120 + backupRetentionDays: 7 + geoRedundantBackup: 'Disabled' + } + } +} + +resource servicePlan 'Microsoft.Web/serverfarms@2021-02-01' = { + name: servicePlanName + location: location + sku: { + name: 'B1' + tier: 'Basic' + size: 'B1' + family: 'B' + capacity: 1 + } + kind: 'linux' + properties: { + perSiteScaling: false + elasticScaleEnabled: false + maximumElasticWorkerCount: 1 + isSpot: false + reserved: true + isXenon: false + hyperV: false + targetWorkerCount: 0 + targetWorkerSizeId: 0 + zoneRedundant: false + } +} + +resource site 'Microsoft.Web/sites@2021-02-01' = { + name: siteName + location: location + kind: 'app,linux,container' + properties: { + enabled: true + hostNameSslStates: [ + { + name: '${siteName}.azurewebsites.net' + sslState: 'Disabled' + hostType: 'Standard' + } + { + name: '${siteName}.scm.azurewebsites.net' + sslState: 'Disabled' + hostType: 'Repository' + } + ] + serverFarmId: servicePlan.id + reserved: true + siteConfig: { + numberOfWorkers: 1 + linuxFxVersion: 'DOCKER|ghcr.io/cgi-fr/iothub-portal:latest' + connectionStrings: [ + { + name: 'IoTHub__ConnectionString' + type: 'Custom' + connectionString: 'HostName=${iotHubName}.azure-devices.net;SharedAccessKeyName=${iotHubOwnerPolicyName};SharedAccessKey=${listKeys(resourceId('Microsoft.Devices/iotHubs/iotHubKeys', iotHubName, iotHubOwnerPolicyName), '2021-07-02').primaryKey}' + } + { + name: 'IoTDPS__ConnectionString' + type: 'Custom' + connectionString: 'HostName=${dpsName}.azure-devices-provisioning.net;SharedAccessKeyName=${provisioningserviceownerPolicyName};SharedAccessKey=${listKeys(resourceId('Microsoft.Devices/provisioningServices/keys', dpsName, provisioningserviceownerPolicyName), '2021-10-15').primaryKey}' + } + { + name: 'StorageAccount__ConnectionString' + type: 'Custom' + connectionString: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};AccountKey=${listKeys(storageAccountId, '2015-05-01-preview').key1}' + } + { + name: 'LoRaKeyManagement__Code' + type: 'Custom' + connectionString: listkeys(functionAppDefaultHost, '2021-02-01').masterKey + } + { + name: 'PostgreSQL__ConnectionString' + type: 'Custom' + connectionString: 'Server=${pgsqlServerName}.postgres.database.azure.com;Database=${siteName};Port=5432;User Id=${pgsqlAdminLogin}@${pgsqlServerName};Password=${pgsqlAdminPassword};Pooling=true;Connection Lifetime=0;Command Timeout=0;Ssl Mode=VerifyFull;' + } + ] + appSettings: [ + { + name: 'IoTDPS__ServiceEndpoint' + value: '${dpsName}.azure-devices-provisioning.net' + } + { + name: 'IoTDPS__IDScope' + value: dps.properties.idScope + } + { + name: 'LoRaKeyManagement__Url' + value: 'https://${functionAppName}.azurewebsites.net' + } + { + name: 'OIDC__ApiClientId' + value: apiClientId + } + { + name: 'OIDC__ClientId' + value: clientId + } + { + name: 'OIDC__Authority' + value: openIdAuthority + } + { + name: 'OIDC__MetadataUrl' + value: openIdMetadataURL + } + { + name: 'OIDC__Scope' + value: iamScopeName + } + { + name: 'LoRaFeature__Enabled' + value: 'true' + } + { + name: 'LoRaRegionRouterConfig__Url' + value: 'https://raw.githubusercontent.com/Azure/iotedge-lorawan-starterkit/dev/Tools/Cli-LoRa-Device-Provisioning/DefaultRouterConfig/' + } + { + name: 'APPINSIGHTS_INSTRUMENTATIONKEY' + value: reference(resourceId('Microsoft.Insights/components', appInsightName), '2020-02-02', 'Full').properties.InstrumentationKey + } + { + name: 'Ideas__Enabled' + value: '${ideasEnabled}' + } + { + name: 'Ideas__Url' + value: ideasUrl + } + { + name: 'Ideas__Authentication__Header' + value: ideasAuthenticationHeader + } + { + name: 'Ideas__Authentication__Token' + value: ideasAuthenticationToken + } + ] + } + httpsOnly: true + storageAccountRequired: false + } +} \ No newline at end of file diff --git a/templates/portalDeployWithLoRa.json b/templates/portalDeployWithLoRa.json deleted file mode 100644 index 55be5280a..000000000 --- a/templates/portalDeployWithLoRa.json +++ /dev/null @@ -1,301 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "location": { - "type": "string", - "metadata": { - "description": "Location for the resources." - } - }, - "uniqueSolutionPrefix": { - "type": "string", - "metadata": { - "description": "Prefix used for resource names. Should be unique as this will also be used for domain names." - } - }, - "pgsqlAdminLogin": { - "type": "string", - "defaultValue": "[concat(uniqueString(resourceGroup().id, newGuid()))]", - "metadata": { - "description": "PostgreSQL user" - } - }, - "pgsqlAdminPassword": { - "type": "securestring", - "defaultValue": "[concat(uniqueString(resourceGroup().id, newGuid()), 'x', '!')]", - "metadata": { - "description": "PostgreSQL password" - } - }, - "openIdAuthority": { - "type": "string", - "metadata": { - "description": "The Open ID Authority" - } - }, - "openIdMetadataURL": { - "type": "string", - "metadata": { - "description": "The Open ID metadata Url from the Identity provider" - } - }, - "clientId": { - "type": "string", - "metadata": { - "description": "The client ID for the B2C tenant" - } - }, - "apiClientId": { - "type": "string", - "metadata": { - "description": "The API client ID for the B2C tenant" - } - }, - "ideasEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "To enable Awesome-Ideas feature when set to true" - } - }, - "ideasUrl": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Url of Awesome-Ideas, to publish ideas submitted by users. Required when ideasEnabled is true" - } - }, - "ideasAuthenticationHeader": { - "type": "string", - "defaultValue": "Ocp-Apim-Subscription-Key", - "metadata": { - "description": "Authentication header to interact with Awesome-Ideas. Required when ideasEnabled is true" - } - }, - "ideasAuthenticationToken": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Authentication token to interact with Awesome-Ideas. Required when ideasEnabled is true" - } - } - }, - "variables": { - "pgsqlServerName": "[concat(parameters('uniqueSolutionPrefix'), 'pgsql')]", - "iotHubName": "[concat(parameters('uniqueSolutionPrefix'), 'hub')]", - "dpsName": "[concat(parameters('uniqueSolutionPrefix'), 'dps')]", - "siteName": "[concat(parameters('uniqueSolutionPrefix'), 'portal')]", - "servicePlanName": "[concat(parameters('uniqueSolutionPrefix'), 'asp')]", - "functionAppName": "[concat(parameters('uniqueSolutionPrefix'), 'function')]", - "storageAccountName": "[concat(parameters('uniqueSolutionPrefix'), 'storage')]", - "iotHubOwnerPolicyName": "iothubowner", - "provisioningserviceownerPolicyName": "provisioningserviceowner", - "deviceImageContainerName": "device-images", - "iamScopeName": "API.Access", - "storageAccountId": "[concat(resourceGroup().id, '/providers/', 'Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]", - "appInsightName": "[concat(parameters('uniqueSolutionPrefix'), 'insight')]", - "functionAppDefaultHost": "[concat(resourceId('Microsoft.Web/sites', variables('functionAppName')), '/host/default/')]" - }, - "resources": [ - { - "type": "Microsoft.Storage/storageAccounts/blobServices/containers", - "apiVersion": "2021-06-01", - "name": "[format('{0}/default/{1}', variables('storageAccountName'), variables('deviceImageContainerName'))]" - }, - { - "type": "Microsoft.Devices/provisioningServices", - "apiVersion": "2021-10-15", - "name": "[variables('dpsName')]", - "location": "[parameters('location')]", - "sku": { - "name": "S1", - "tier": "Standard", - "capacity": 1 - }, - "properties": { - "state": "Active", - "iotHubs": [ - { - "connectionString": "[concat('HostName=', variables('iotHubName'), '.azure-devices.net;SharedAccessKeyName=', variables('iotHubOwnerPolicyName'), ';SharedAccessKey=', listKeys(resourceId('Microsoft.Devices/IotHubs/IotHubKeys', variables('iotHubName'), variables('iotHubOwnerPolicyName')), '2021-07-02').primaryKey)]", - "location": "[parameters('location')]" - } - ], - "allocationPolicy": "Hashed" - } - }, - { - "type": "Microsoft.DBforPostgreSQL/servers", - "apiVersion": "2017-12-01", - "name": "[variables('pgsqlServerName')]", - "location": "[parameters('location')]", - "sku": { - "name": "B_Gen5_2", - "tier": "Basic", - "capacity": 2, - "size": "5120", - "family": "Gen5" - }, - "properties": { - "createMode": "Default", - "version": "11", - "administratorLogin": "[parameters('pgsqlAdminLogin')]", - "administratorLoginPassword": "[parameters('pgsqlAdminPassword')]", - "storageProfile": { - "storageMB": 5120, - "backupRetentionDays": 7, - "geoRedundantBackup": "Disabled" - } - } - }, - { - "type": "Microsoft.Web/serverfarms", - "apiVersion": "2021-02-01", - "name": "[variables('servicePlanName')]", - "location": "[parameters('location')]", - "sku": { - "name": "B1", - "tier": "Basic", - "size": "B1", - "family": "B", - "capacity": 1 - }, - "kind": "linux", - "properties": { - "perSiteScaling": false, - "elasticScaleEnabled": false, - "maximumElasticWorkerCount": 1, - "isSpot": false, - "reserved": true, - "isXenon": false, - "hyperV": false, - "targetWorkerCount": 0, - "targetWorkerSizeId": 0, - "zoneRedundant": false - } - }, - { - "type": "Microsoft.Web/sites", - "apiVersion": "2021-02-01", - "name": "[variables('siteName')]", - "location": "[parameters('location')]", - "dependsOn": [ - "[resourceId('Microsoft.Devices/provisioningServices', variables('dpsName'))]", - "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]" - ], - "kind": "app,linux,container", - "properties": { - "enabled": true, - "hostNameSslStates": [ - { - "name": "[concat(variables('siteName'), '.azurewebsites.net')]", - "sslState": "Disabled", - "hostType": "Standard" - }, - { - "name": "[concat(variables('siteName'), '.scm.azurewebsites.net')]", - "sslState": "Disabled", - "hostType": "Repository" - } - ], - "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]", - "reserved": true, - "siteConfig": { - "numberOfWorkers": 1, - "linuxFxVersion": "DOCKER|ghcr.io/cgi-fr/iothub-portal:latest", - "connectionStrings": [ - { - "name": "IoTHub__ConnectionString", - "type": "Custom", - "connectionString": "[concat('HostName=', variables('iotHubName'), '.azure-devices.net;SharedAccessKeyName=', variables('iotHubOwnerPolicyName'), ';SharedAccessKey=', listKeys(resourceId('Microsoft.Devices/iotHubs/iotHubKeys', variables('iotHubName'), variables('iotHubOwnerPolicyName')), '2021-07-02').primaryKey)]" - }, - { - "name": "IoTDPS__ConnectionString", - "type": "Custom", - "connectionString": "[concat('HostName=', variables('dpsName'), '.azure-devices-provisioning.net;SharedAccessKeyName=', variables('provisioningserviceownerPolicyName'), ';SharedAccessKey=', listKeys(resourceId('Microsoft.Devices/provisioningServices/keys', variables('dpsName'), variables('provisioningserviceownerPolicyName')), '2021-10-15').primaryKey)]" - }, - { - "name": "StorageAccount__ConnectionString", - "type": "Custom", - "connectionString": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountId'),'2015-05-01-preview').key1)]" - }, - { - "name": "LoRaKeyManagement__Code", - "type": "Custom", - "connectionString": "[listkeys(variables('functionAppDefaultHost'),'2021-02-01').masterKey]" - }, - { - "name": "PostgreSQL__ConnectionString", - "type": "Custom", - "connectionString": "[concat('Server=', variables('pgsqlServerName') ,'.postgres.database.azure.com;Database=', variables('siteName') ,';Port=5432;User Id=', parameters('pgsqlAdminLogin') ,'@', variables('pgsqlServerName') ,';Password=', parameters('pgsqlAdminPassword'), ';Pooling=true;Connection Lifetime=0;Command Timeout=0;Ssl Mode=VerifyFull;')]" - } - ], - "appSettings": [ - { - "name": "IoTDPS__ServiceEndpoint", - "value": "[concat(variables('dpsName'), '.azure-devices-provisioning.net')]" - }, - { - "name": "IoTDPS__IDScope", - "value": "[reference(variables('dpsName')).idScope]" - }, - { - "name": "LoRaKeyManagement__Url", - "value": "[concat('https://', variables('functionAppName'), '.azurewebsites.net')]" - }, - { - "name": "OIDC__ApiClientId", - "value": "[parameters('apiClientId')]" - }, - { - "name": "OIDC__ClientId", - "value": "[parameters('clientId')]" - }, - { - "name": "OIDC__Authority", - "value": "[parameters('openIdAuthority')]" - }, - { - "name": "OIDC__MetadataUrl", - "value": "[parameters('openIdMetadataURL')]" - }, - { - "name": "OIDC__Scope", - "value": "[variables('iamScopeName')]" - }, - { - "name": "LoRaFeature__Enabled", - "value": "true" - }, - { - "name": "LoRaRegionRouterConfig__Url", - "value": "https://raw.githubusercontent.com/Azure/iotedge-lorawan-starterkit/dev/Tools/Cli-LoRa-Device-Provisioning/DefaultRouterConfig/" - }, - { - "name": "APPINSIGHTS_INSTRUMENTATIONKEY", - "value": "[reference(resourceId('Microsoft.Insights/components', variables('appInsightName')), '2020-02-02', 'Full').properties.InstrumentationKey]" - }, - { - "name": "Ideas__Enabled", - "value": "[parameters('ideasEnabled')]" - }, - { - "name": "Ideas__Url", - "value": "[parameters('ideasUrl')]" - }, - { - "name": "Ideas__Authentication__Header", - "value": "[parameters('ideasAuthenticationHeader')]" - }, - { - "name": "Ideas__Authentication__Token", - "value": "[parameters('ideasAuthenticationToken')]" - } - ] - }, - "httpsOnly": true, - "storageAccountRequired": false - } - } - ] -} \ No newline at end of file diff --git a/templates/portalDeployWithoutLoRa.bicep b/templates/portalDeployWithoutLoRa.bicep new file mode 100644 index 000000000..8692a6a52 --- /dev/null +++ b/templates/portalDeployWithoutLoRa.bicep @@ -0,0 +1,267 @@ +@description('Location for the resources.') +param location string + +@description('Prefix used for resource names. Should be unique as this will also be used for domain names.') +param uniqueSolutionPrefix string + +@description('PostgreSQL user') +param pgsqlAdminLogin string = concat(uniqueString(resourceGroup().id, newGuid())) + +@description('PostgreSQL password') +@secure() +param pgsqlAdminPassword string = '${uniqueString(resourceGroup().id, newGuid())}x!' + +@description('The Open ID Authority') +param openIdAuthority string + +@description('The Open ID metadata Url from the Identity provider') +param openIdMetadataURL string + +@description('The client ID for the B2C tenant') +param clientId string + +@description('The API client ID for the B2C tenant') +param apiClientId string + +@description('To enable Awesome-Ideas feature when set to true') +param ideasEnabled bool = false + +@description('Url of Awesome-Ideas, to publish ideas submitted by users. Required when ideasEnabled is true') +param ideasUrl string = '' + +@description('Authentication header to interact with Awesome-Ideas. Required when ideasEnabled is true') +param ideasAuthenticationHeader string = 'Ocp-Apim-Subscription-Key' + +@description('Authentication token to interact with Awesome-Ideas. Required when ideasEnabled is true') +param ideasAuthenticationToken string = '' + +var pgsqlServerName = '${uniqueSolutionPrefix}pgsql' +var iotHubName = '${uniqueSolutionPrefix}hub' +var dpsName = '${uniqueSolutionPrefix}dps' +var siteName = '${uniqueSolutionPrefix}portal' +var servicePlanName = '${uniqueSolutionPrefix}asp' +var storageAccountName = '${uniqueSolutionPrefix}storage' +var iotHubOwnerPolicyName = 'iothubowner' +var provisioningserviceownerPolicyName = 'provisioningserviceowner' +var deviceImageContainerName = 'device-images' +var iamScopeName = 'API.Access' +var storageAccountId = '${resourceGroup().id}/providers/Microsoft.Storage/storageAccounts/${storageAccountName}' +var appInsightName = '${uniqueSolutionPrefix}insight' + +resource iotHub 'Microsoft.Devices/IotHubs@2021-07-02' = { + sku: { + name: 'S1' + capacity: 1 + } + name: iotHubName + location: location + properties: { + } + dependsOn: [] +} + +resource dps 'Microsoft.Devices/provisioningServices@2021-10-15' = { + name: dpsName + location: location + sku: { + name: 'S1' + capacity: 1 + } + properties: { + state: 'Active' + iotHubs: [ + { + connectionString: 'HostName=${iotHubName}.azure-devices.net;SharedAccessKeyName=${iotHubOwnerPolicyName};SharedAccessKey=${listKeys(resourceId('Microsoft.Devices/IotHubs/IotHubKeys', iotHubName, iotHubOwnerPolicyName), '2021-07-02').primaryKey}' + location: location + } + ] + allocationPolicy: 'Hashed' + } + dependsOn: [ + iotHub + ] +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { + name: storageAccountName + location: location + kind: 'StorageV2' + sku: { + name: 'Standard_LRS' + } + dependsOn: [] +} + +resource storageAccountName_default_deviceImageContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2022-05-01' = { + name: '${storageAccountName}/default/${deviceImageContainerName}' + dependsOn: [ + storageAccount + ] +} + +resource pgsqlServer 'Microsoft.DBforPostgreSQL/servers@2017-12-01' = { + name: pgsqlServerName + location: location + sku: { + name: 'B_Gen5_2' + tier: 'Basic' + capacity: 2 + size: '5120' + family: 'Gen5' + } + properties: { + createMode: 'Default' + version: '11' + administratorLogin: pgsqlAdminLogin + administratorLoginPassword: pgsqlAdminPassword + storageProfile: { + storageMB: 5120 + backupRetentionDays: 7 + geoRedundantBackup: 'Disabled' + } + } +} + +resource appInsight 'Microsoft.Insights/components@2020-02-02' = { + kind: 'web' + name: appInsightName + location: location + properties: { + Application_Type: 'web' + } + dependsOn: [] +} + +resource servicePlan 'Microsoft.Web/serverfarms@2021-03-01' = { + name: servicePlanName + location: location + sku: { + name: 'B1' + tier: 'Basic' + size: 'B1' + family: 'B' + capacity: 1 + } + kind: 'linux' + properties: { + perSiteScaling: false + elasticScaleEnabled: false + maximumElasticWorkerCount: 1 + isSpot: false + reserved: true + isXenon: false + hyperV: false + targetWorkerCount: 0 + targetWorkerSizeId: 0 + zoneRedundant: false + } +} + +resource site 'Microsoft.Web/sites@2021-03-01' = { + name: siteName + location: location + kind: 'app,linux,container' + properties: { + enabled: true + hostNameSslStates: [ + { + name: '${siteName}.azurewebsites.net' + sslState: 'Disabled' + hostType: 'Standard' + } + { + name: '${siteName}.scm.azurewebsites.net' + sslState: 'Disabled' + hostType: 'Repository' + } + ] + serverFarmId: servicePlan.id + reserved: true + siteConfig: { + numberOfWorkers: 1 + linuxFxVersion: 'DOCKER|ghcr.io/cgi-fr/iothub-portal:latest' + connectionStrings: [ + { + name: 'IoTHub__ConnectionString' + type: 'Custom' + connectionString: 'HostName=${iotHubName}.azure-devices.net;SharedAccessKeyName=${iotHubOwnerPolicyName};SharedAccessKey=${listKeys(resourceId('Microsoft.Devices/iotHubs/iotHubKeys', iotHubName, iotHubOwnerPolicyName), '2021-07-02').primaryKey}' + } + { + name: 'IoTDPS__ConnectionString' + type: 'Custom' + connectionString: 'HostName=${dpsName}.azure-devices-provisioning.net;SharedAccessKeyName=${provisioningserviceownerPolicyName};SharedAccessKey=${listKeys(resourceId('Microsoft.Devices/provisioningServices/keys', dpsName, provisioningserviceownerPolicyName), '2021-10-15').primaryKey}' + } + { + name: 'StorageAccount__ConnectionString' + type: 'Custom' + connectionString: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};AccountKey=${listKeys(storageAccountId, '2015-05-01-preview').key1}' + } + { + name: 'PostgreSQL__ConnectionString' + type: 'Custom' + connectionString: 'Server=${pgsqlServerName}.postgres.database.azure.com;Database=${siteName};Port=5432;User Id=${pgsqlAdminLogin}@${pgsqlServerName};Password=${pgsqlAdminPassword};Pooling=true;Connection Lifetime=0;Command Timeout=0;Ssl Mode=VerifyFull;' + } + ] + appSettings: [ + { + name: 'IoTDPS__ServiceEndpoint' + value: '${dpsName}.azure-devices-provisioning.net' + } + { + name: 'IoTDPS__IDScope' + value: dps.properties.idScope + } + { + name: 'OIDC__ApiClientId' + value: apiClientId + } + { + name: 'OIDC__ClientId' + value: clientId + } + { + name: 'OIDC__Authority' + value: openIdAuthority + } + { + name: 'OIDC__MetadataUrl' + value: openIdMetadataURL + } + { + name: 'OIDC__Scope' + value: iamScopeName + } + { + name: 'LoRaFeature__Enabled' + value: 'false' + } + { + name: 'APPINSIGHTS_INSTRUMENTATIONKEY' + value: reference(appInsight.id, '2020-02-02', 'Full').properties.InstrumentationKey + } + { + name: 'Ideas__Enabled' + value: '${ideasEnabled}' + } + { + name: 'Ideas__Url' + value: ideasUrl + } + { + name: 'Ideas__Authentication__Header' + value: ideasAuthenticationHeader + } + { + name: 'Ideas__Authentication__Token' + value: ideasAuthenticationToken + } + ] + } + httpsOnly: true + } + dependsOn: [ + + storageAccount + + ] +} \ No newline at end of file diff --git a/templates/portalDeployWithoutLoRa.json b/templates/portalDeployWithoutLoRa.json deleted file mode 100644 index 80351f234..000000000 --- a/templates/portalDeployWithoutLoRa.json +++ /dev/null @@ -1,333 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "location": { - "type": "string", - "metadata": { - "description": "Location for the resources." - } - }, - "uniqueSolutionPrefix": { - "type": "string", - "metadata": { - "description": "Prefix used for resource names. Should be unique as this will also be used for domain names." - } - }, - "pgsqlAdminLogin": { - "type": "string", - "defaultValue": "[concat(uniqueString(resourceGroup().id, newGuid()))]", - "metadata": { - "description": "PostgreSQL user" - } - }, - "pgsqlAdminPassword": { - "type": "securestring", - "defaultValue": "[concat(uniqueString(resourceGroup().id, newGuid()), 'x', '!')]", - "metadata": { - "description": "PostgreSQL password" - } - }, - "openIdAuthority": { - "type": "string", - "metadata": { - "description": "The Open ID Authority" - } - }, - "openIdMetadataURL": { - "type": "string", - "metadata": { - "description": "The Open ID metadata Url from the Identity provider" - } - }, - "clientId": { - "type": "string", - "metadata": { - "description": "The client ID for the B2C tenant" - } - }, - "apiClientId": { - "type": "string", - "metadata": { - "description": "The API client ID for the B2C tenant" - } - }, - "ideasEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "To enable Awesome-Ideas feature when set to true" - } - }, - "ideasUrl": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Url of Awesome-Ideas, to publish ideas submitted by users. Required when ideasEnabled is true" - } - }, - "ideasAuthenticationHeader": { - "type": "string", - "defaultValue": "Ocp-Apim-Subscription-Key", - "metadata": { - "description": "Authentication header to interact with Awesome-Ideas. Required when ideasEnabled is true" - } - }, - "ideasAuthenticationToken": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Authentication token to interact with Awesome-Ideas. Required when ideasEnabled is true" - } - } - }, - "variables": { - "pgsqlServerName": "[concat(parameters('uniqueSolutionPrefix'), 'pgsql')]", - "iotHubName": "[concat(parameters('uniqueSolutionPrefix'), 'hub')]", - "dpsName": "[concat(parameters('uniqueSolutionPrefix'), 'dps')]", - "siteName": "[concat(parameters('uniqueSolutionPrefix'), 'portal')]", - "servicePlanName": "[concat(parameters('uniqueSolutionPrefix'), 'asp')]", - "storageAccountName": "[concat(parameters('uniqueSolutionPrefix'), 'storage')]", - "storageAccountType": "Standard_LRS", - "iotHubOwnerPolicyName": "iothubowner", - "provisioningserviceownerPolicyName": "provisioningserviceowner", - "deviceImageContainerName": "device-images", - "iamScopeName": "API.Access", - "storageAccountId": "[concat(resourceGroup().id, '/providers/', 'Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]", - "appInsightName": "[concat(parameters('uniqueSolutionPrefix'), 'insight')]" - }, - "resources": [ - { - "type": "Microsoft.Devices/IotHubs", - "apiVersion": "2021-07-02", - "sku": { - "name": "S1", - "tier": "Standard", - "capacity": 1 - }, - "name": "[variables('iotHubName')]", - "location": "[parameters('location')]", - "properties": {}, - "dependsOn": [] - }, - { - "type": "Microsoft.Devices/provisioningServices", - "apiVersion": "2021-10-15", - "name": "[variables('dpsName')]", - "location": "[parameters('location')]", - "sku": { - "name": "S1", - "tier": "Standard", - "capacity": 1 - }, - "properties": { - "state": "Active", - "iotHubs": [ - { - "connectionString": "[concat('HostName=', variables('iotHubName'), '.azure-devices.net;SharedAccessKeyName=', variables('iotHubOwnerPolicyName'), ';SharedAccessKey=', listKeys(resourceId('Microsoft.Devices/IotHubs/IotHubKeys', variables('iotHubName'), variables('iotHubOwnerPolicyName')), '2021-07-02').primaryKey)]", - "location": "[parameters('location')]" - } - ], - "allocationPolicy": "Hashed" - }, - "dependsOn": [ - "[resourceId('Microsoft.Devices/iothubs/', variables('iotHubName'))]" - ] - }, - { - "type": "Microsoft.Storage/storageAccounts", - "name": "[variables('storageAccountName')]", - "apiVersion": "2021-09-01", - "location": "[parameters('location')]", - "sku": { - "name": "Standard_LRS", - "tier": "Standard" - }, - "properties": { - "accountType": "[variables('storageAccountType')]" - }, - "dependsOn": [] - }, - { - "type": "Microsoft.Storage/storageAccounts/blobServices/containers", - "apiVersion": "2021-09-01", - "name": "[format('{0}/default/{1}', variables('storageAccountName'), variables('deviceImageContainerName'))]", - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]" - ] - }, - { - "type": "Microsoft.DBforPostgreSQL/servers", - "apiVersion": "2017-12-01", - "name": "[variables('pgsqlServerName')]", - "location": "[parameters('location')]", - "sku": { - "name": "B_Gen5_2", - "tier": "Basic", - "capacity": 2, - "size": "5120", - "family": "Gen5" - }, - "properties": { - "createMode": "Default", - "version": "11", - "administratorLogin": "[parameters('pgsqlAdminLogin')]", - "administratorLoginPassword": "[parameters('pgsqlAdminPassword')]", - "storageProfile": { - "storageMB": 5120, - "backupRetentionDays": 7, - "geoRedundantBackup": "Disabled" - } - } - }, - { - "type": "Microsoft.Insights/components", - "kind": "web", - "name": "[variables('appInsightName')]", - "apiVersion": "2020-02-02", - "location": "[parameters('location')]", - "scale": null, - "properties": { - "Application_Type": "web" - }, - "dependsOn": [] - }, - { - "type": "Microsoft.Web/serverfarms", - "apiVersion": "2021-03-01", - "name": "[variables('servicePlanName')]", - "location": "[parameters('location')]", - "sku": { - "name": "B1", - "tier": "Basic", - "size": "B1", - "family": "B", - "capacity": 1 - }, - "kind": "linux", - "properties": { - "perSiteScaling": false, - "elasticScaleEnabled": false, - "maximumElasticWorkerCount": 1, - "isSpot": false, - "reserved": true, - "isXenon": false, - "hyperV": false, - "targetWorkerCount": 0, - "targetWorkerSizeId": 0, - "zoneRedundant": false - } - }, - { - "type": "Microsoft.Web/sites", - "apiVersion": "2021-03-01", - "name": "[variables('siteName')]", - "location": "[parameters('location')]", - "dependsOn": [ - "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]", - "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]", - "[resourceId('Microsoft.Devices/provisioningServices', variables('dpsName'))]", - "[resourceId('microsoft.insights/components/', variables('appInsightName'))]" - ], - "kind": "app,linux,container", - "properties": { - "enabled": true, - "hostNameSslStates": [ - { - "name": "[concat(variables('siteName'), '.azurewebsites.net')]", - "sslState": "Disabled", - "hostType": "Standard" - }, - { - "name": "[concat(variables('siteName'), '.scm.azurewebsites.net')]", - "sslState": "Disabled", - "hostType": "Repository" - } - ], - "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]", - "reserved": true, - "siteConfig": { - "numberOfWorkers": 1, - "linuxFxVersion": "DOCKER|ghcr.io/cgi-fr/iothub-portal:latest", - "connectionStrings": [ - { - "name": "IoTHub__ConnectionString", - "type": "Custom", - "connectionString": "[concat('HostName=', variables('iotHubName'), '.azure-devices.net;SharedAccessKeyName=', variables('iotHubOwnerPolicyName'), ';SharedAccessKey=', listKeys(resourceId('Microsoft.Devices/iotHubs/iotHubKeys', variables('iotHubName'), variables('iotHubOwnerPolicyName')), '2021-07-02').primaryKey)]" - }, - { - "name": "IoTDPS__ConnectionString", - "type": "Custom", - "connectionString": "[concat('HostName=', variables('dpsName'), '.azure-devices-provisioning.net;SharedAccessKeyName=', variables('provisioningserviceownerPolicyName'), ';SharedAccessKey=', listKeys(resourceId('Microsoft.Devices/provisioningServices/keys', variables('dpsName'), variables('provisioningserviceownerPolicyName')), '2021-10-15').primaryKey)]" - }, - { - "name": "StorageAccount__ConnectionString", - "type": "Custom", - "connectionString": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountId'),'2015-05-01-preview').key1)]" - }, - { - "name": "PostgreSQL__ConnectionString", - "type": "Custom", - "connectionString": "[concat('Server=', variables('pgsqlServerName') ,'.postgres.database.azure.com;Database=', variables('siteName') ,';Port=5432;User Id=', parameters('pgsqlAdminLogin') ,'@', variables('pgsqlServerName') ,';Password=', parameters('pgsqlAdminPassword'), ';Pooling=true;Connection Lifetime=0;Command Timeout=0;Ssl Mode=VerifyFull;')]" - } - ], - "appSettings": [ - { - "name": "IoTDPS__ServiceEndpoint", - "value": "[concat(variables('dpsName'), '.azure-devices-provisioning.net')]" - }, - { - "name": "IoTDPS__IDScope", - "value": "[reference(variables('dpsName')).idScope]" - }, - { - "name": "OIDC__ApiClientId", - "value": "[parameters('apiClientId')]" - }, - { - "name": "OIDC__ClientId", - "value": "[parameters('clientId')]" - }, - { - "name": "OIDC__Authority", - "value": "[parameters('openIdAuthority')]" - }, - { - "name": "OIDC__MetadataUrl", - "value": "[parameters('openIdMetadataURL')]" - }, - { - "name": "OIDC__Scope", - "value": "[variables('iamScopeName')]" - }, - { - "name": "LoRaFeature__Enabled", - "value": "false" - }, - { - "name": "APPINSIGHTS_INSTRUMENTATIONKEY", - "value": "[reference(resourceId('Microsoft.Insights/components', variables('appInsightName')), '2020-02-02', 'Full').properties.InstrumentationKey]" - }, - { - "name": "Ideas__Enabled", - "value": "[parameters('ideasEnabled')]" - }, - { - "name": "Ideas__Url", - "value": "[parameters('ideasUrl')]" - }, - { - "name": "Ideas__Authentication__Header", - "value": "[parameters('ideasAuthenticationHeader')]" - }, - { - "name": "Ideas__Authentication__Token", - "value": "[parameters('ideasAuthenticationToken')]" - } - ] - }, - "httpsOnly": true - } - } - ] -} \ No newline at end of file From 55ec35f6310333154dca45693b18588940d23673 Mon Sep 17 00:00:00 2001 From: Kevin BEAUGRAND Date: Wed, 16 Nov 2022 13:02:04 +0100 Subject: [PATCH 2/2] Publish ARM template to release artifacts --- .github/workflows/publish.yml | 68 +++++++++++++++++++++++++++++++++-- README.md | 2 +- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 00cab23c5..40e80cfe4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,7 +15,6 @@ env: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - # This workflow contains a single job called "build" build: name: Build & Push Solution # The type of runner that the job will run on @@ -67,4 +66,69 @@ jobs: BUILD_VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }} GITHUB_RUN_NUMBER=${{ github.run_number }} tags: - ${{ steps.meta.outputs.tags }} \ No newline at end of file + ${{ steps.meta.outputs.tags }} + + arm_templates: + name: Publish ARM templates + # The type of runner that the job will run on + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3.1.0 + with: + path: IoT-Hub-Portal + + - name: Checkout Azure/iotedge-lorawan-starterkit + uses: actions/checkout@v3.1.0 + with: + repository: Azure/iotedge-lorawan-starterkit + ref: dev + path: iotedge-lorawan-starterkit + + - uses: actions/checkout@v3.1.0 + with: + ref: arm/main + path: arm-templates + + - name: Generate app token + id: generate_app_token + uses: tibdex/github-app-token@v1 + with: + app_id: ${{ secrets.BOT_APP_ID }} + private_key: ${{ secrets.BOT_PRIVATE_KEY }} + + - name: Copy templates + working-directory: arm-templates + run: | + rm -rf ./templates/ + mkdir -p templates/iotedge-lorawan-starterkit + touch ./templates/.gitkeep + cp -r ../IoT-Hub-Portal/templates/* ./templates/ + cp -r ../iotedge-lorawan-starterkit/TemplateBicep/* ./templates/iotedge-lorawan-starterkit/ + sed -r -i 's/..\/iotedge-lorawan-starterkit\/TemplateBicep\/main.bicep/.\/iotedge-lorawan-starterkit\/main.bicep/g' ./templates/azuredeploy.bicep + + - name: Generate ARM file + working-directory: arm-templates/templates + run: az bicep build --file azuredeploy.bicep --outfile azuredeploy.json + + - name: Upload templates to release assets + uses: AButler/upload-release-assets@v2.0 + with: + files: 'arm-templates/templates/*.json' + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Create PR for template updates + uses: peter-evans/create-pull-request@v4 + with: + base: 'arm/main' + branch: 'arm/feature/update-templates' + path: 'arm-templates' + commit-message: 'Update templates from release.' + token: ${{ steps.generate_app_token.outputs.token }} + title: '[ARM Templates] Update templates from release' + labels: | + arm-templates + automated pr + team-reviewers: '@CGI-FR/azure-iot-portal-authors' + body: | + Automated changes to the Azure templates. diff --git a/README.md b/README.md index d64b01ecf..4d9b24898 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ The template will deploy in your Azure subscription the Following resources: 1. Press on the button here below to start your Azure Deployment. - [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FCGI-FR%2FIoT-Hub-Portal%2Fmain%2Ftemplates%2Fazuredeploy.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FCGI-FR%2FIoT-Hub-Portal%2Fmain%2Ftemplates%2FazuredeployUI.json) + [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FCGI-FR%2FIoT-Hub-Portal%2Farm%2Fmain%2Ftemplates%2Fazuredeploy.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FCGI-FR%2FIoT-Hub-Portal%2Farm%2Fmain%2Ftemplates%2FazuredeployUI.json) 1. You will get to a page asking you to fill the following fields : * **Resource Group**: A logical "folder" where all the template resource would be put into, just choose a meaningful name.