From 94ae6d1a0dde00a6e5f68f94faa89f23ad073668 Mon Sep 17 00:00:00 2001 From: snoejovich <92036998+snoejovich@users.noreply.github.com> Date: Wed, 10 Apr 2024 09:38:25 -0500 Subject: [PATCH 1/2] feat: `avm/res/service-fabric/cluster` (#1393) ## Description Migrate bicep module for service-fabric cluster from CARML. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.service-fabric.cluster](https://github.com/snoejovich/bicep-registry-modules/actions/workflows/avm.res.service-fabric.cluster.yml/badge.svg?branch=feature-sn-newmigrateservicefabric)](https://github.com/snoejovich/bicep-registry-modules/actions/workflows/avm.res.service-fabric.cluster.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Zach Trocinski <30884663+oZakari@users.noreply.github.com> Co-authored-by: Alexander Sehr Co-authored-by: Luke Snoddy <37806411+lsnoddy@users.noreply.github.com> Co-authored-by: ChrisSidebotham-MSFT <48600046+ChrisSidebotham@users.noreply.github.com> --- .github/CODEOWNERS | 2 +- .github/ISSUE_TEMPLATE/avm_module_issue.yml | 2 +- .../avm.res.service-fabric.cluster.yml | 85 + avm/res/service-fabric/cluster/README.md | 1536 +++++++++++++++++ .../cluster/application-type/README.md | 71 + .../cluster/application-type/main.bicep | 31 + .../cluster/application-type/main.json | 77 + avm/res/service-fabric/cluster/main.bicep | 479 +++++ avm/res/service-fabric/cluster/main.json | 677 ++++++++ .../cluster/tests/e2e/cert/main.test.bicep | 71 + .../tests/e2e/defaults/main.test.bicep | 67 + .../cluster/tests/e2e/max/dependencies.bicep | 31 + .../cluster/tests/e2e/max/main.test.bicep | 238 +++ .../tests/e2e/waf-aligned/dependencies.bicep | 31 + .../tests/e2e/waf-aligned/main.test.bicep | 214 +++ avm/res/service-fabric/cluster/version.json | 7 + 16 files changed, 3617 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/avm.res.service-fabric.cluster.yml create mode 100644 avm/res/service-fabric/cluster/README.md create mode 100644 avm/res/service-fabric/cluster/application-type/README.md create mode 100644 avm/res/service-fabric/cluster/application-type/main.bicep create mode 100644 avm/res/service-fabric/cluster/application-type/main.json create mode 100644 avm/res/service-fabric/cluster/main.bicep create mode 100644 avm/res/service-fabric/cluster/main.json create mode 100644 avm/res/service-fabric/cluster/tests/e2e/cert/main.test.bicep create mode 100644 avm/res/service-fabric/cluster/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/service-fabric/cluster/tests/e2e/max/dependencies.bicep create mode 100644 avm/res/service-fabric/cluster/tests/e2e/max/main.test.bicep create mode 100644 avm/res/service-fabric/cluster/tests/e2e/waf-aligned/dependencies.bicep create mode 100644 avm/res/service-fabric/cluster/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/service-fabric/cluster/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index da03f81c8a..aeceb727dd 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -126,7 +126,7 @@ #/avm/res/resources/tags/ @Azure/avm-res-resources-tags-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/search/search-service/ @Azure/avm-res-search-searchservice-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/service-bus/namespace/ @Azure/avm-res-servicebus-namespace-module-owners-bicep @Azure/avm-core-team-technical-bicep -#/avm/res/service-fabric/cluster/ @Azure/avm-res-servicefabric-cluster-module-owners-bicep @Azure/avm-core-team-technical-bicep +/avm/res/service-fabric/cluster/ @Azure/avm-res-servicefabric-cluster-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/signal-r-service/signal-r/ @Azure/avm-res-signalrservice-signalr-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/signal-r-service/web-pub-sub/ @Azure/avm-res-signalrservice-webpubsub-module-owners-bicep @Azure/avm-core-team-technical-bicep #/avm/res/sql/managed-instance/ @Azure/avm-res-sql-managedinstance-module-owners-bicep @Azure/avm-core-team-technical-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index cd6d3f20ce..a1f30bffbc 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -159,7 +159,7 @@ body: # - "avm/res/resources/tags" - "avm/res/search/search-service" - "avm/res/service-bus/namespace" - # - "avm/res/service-fabric/cluster" + - "avm/res/service-fabric/cluster" - "avm/res/signal-r-service/signal-r" - "avm/res/signal-r-service/web-pub-sub" # - "avm/res/sql/managed-instance" diff --git a/.github/workflows/avm.res.service-fabric.cluster.yml b/.github/workflows/avm.res.service-fabric.cluster.yml new file mode 100644 index 0000000000..93c11bbf84 --- /dev/null +++ b/.github/workflows/avm.res.service-fabric.cluster.yml @@ -0,0 +1,85 @@ +name: "avm.res.service-fabric.cluster" + +on: + schedule: + - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.service-fabric.cluster.yml" + - "avm/res/service-fabric/cluster/**" + - "avm/utilities/pipelines/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/service-fabric/cluster" + workflowPath: ".github/workflows/avm.res.service-fabric.cluster.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get parameter file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/service-fabric/cluster/README.md b/avm/res/service-fabric/cluster/README.md new file mode 100644 index 0000000000..eba3b7a0b5 --- /dev/null +++ b/avm/res/service-fabric/cluster/README.md @@ -0,0 +1,1536 @@ +# Service Fabric Clusters `[Microsoft.ServiceFabric/clusters]` + +This module deploys a Service Fabric Cluster. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.ServiceFabric/clusters` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ServiceFabric/2021-06-01/clusters) | +| `Microsoft.ServiceFabric/clusters/applicationTypes` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ServiceFabric/2021-06-01/clusters/applicationTypes) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/service-fabric/cluster:`. + +- [Certificate](#example-1-certificate) +- [Using only defaults](#example-2-using-only-defaults) +- [Using large parameter set](#example-3-using-large-parameter-set) +- [WAF-aligned](#example-4-waf-aligned) + +### Example 1: _Certificate_ + +This instance deploys the module with a certificate. + + +
+ +via Bicep module + +```bicep +module cluster 'br/public:avm/res/service-fabric/cluster:' = { + name: 'clusterDeployment' + params: { + // Required parameters + managementEndpoint: 'https://sfccer001.westeurope.cloudapp.azure.com:19080' + name: 'sfccer001' + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + name: 'Node01' + } + ] + reliabilityLevel: 'None' + // Non-required parameters + certificate: { + thumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + x509StoreName: 'My' + } + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "managementEndpoint": { + "value": "https://sfccer001.westeurope.cloudapp.azure.com:19080" + }, + "name": { + "value": "sfccer001" + }, + "nodeTypes": { + "value": [ + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Bronze", + "ephemeralPorts": { + "endPort": 65534, + "startPort": 49152 + }, + "httpGatewayEndpointPort": 19080, + "isPrimary": true, + "name": "Node01" + } + ] + }, + "reliabilityLevel": { + "value": "None" + }, + // Non-required parameters + "certificate": { + "value": { + "thumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130", + "x509StoreName": "My" + } + }, + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 2: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module cluster 'br/public:avm/res/service-fabric/cluster:' = { + name: 'clusterDeployment' + params: { + // Required parameters + managementEndpoint: 'https://sfcmin001.westeurope.cloudapp.azure.com:19080' + name: 'sfcmin001' + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + name: 'Node01' + } + ] + reliabilityLevel: 'None' + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "managementEndpoint": { + "value": "https://sfcmin001.westeurope.cloudapp.azure.com:19080" + }, + "name": { + "value": "sfcmin001" + }, + "nodeTypes": { + "value": [ + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Bronze", + "ephemeralPorts": { + "endPort": 65534, + "startPort": 49152 + }, + "httpGatewayEndpointPort": 19080, + "isPrimary": true, + "name": "Node01" + } + ] + }, + "reliabilityLevel": { + "value": "None" + }, + // Non-required parameters + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 3: _Using large parameter set_ + +This instance deploys the module with most of its features enabled. + + +

+ +via Bicep module + +```bicep +module cluster 'br/public:avm/res/service-fabric/cluster:' = { + name: 'clusterDeployment' + params: { + // Required parameters + managementEndpoint: 'https://sfcmax001.westeurope.cloudapp.azure.com:19080' + name: 'sfcmax001' + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Silver' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + isStateless: false + multipleAvailabilityZones: false + name: 'Node01' + placementProperties: {} + reverseProxyEndpointPort: '' + vmInstanceCount: 5 + } + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 64000 + httpGatewayEndpointPort: 19007 + isPrimary: true + name: 'Node02' + startPort: 49000 + vmInstanceCount: 5 + } + } + ] + reliabilityLevel: 'Silver' + // Non-required parameters + addOnFeatures: [ + 'BackupRestoreService' + 'DnsService' + 'RepairManager' + 'ResourceMonitorService' + ] + applicationTypes: [ + { + name: 'WordCount' + } + ] + azureActiveDirectory: { + clientApplication: '' + clusterApplication: 'cf33fea8-b30f-424f-ab73-c48d99e0b222' + tenantId: '' + } + certificateCommonNames: { + commonNames: [ + { + certificateCommonName: 'certcommon' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + } + ] + x509StoreName: '' + } + clientCertificateCommonNames: [ + { + certificateCommonName: 'clientcommoncert1' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateCommonName: 'clientcommoncert2' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + clientCertificateThumbprints: [ + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + diagnosticsStorageAccountConfig: { + blobEndpoint: '' + protectedAccountKeyName: 'StorageAccountKey1' + queueEndpoint: '' + storageAccountName: '' + tableEndpoint: '' + } + fabricSettings: [ + { + name: 'Security' + parameters: [ + { + name: 'ClusterProtectionLevel' + value: 'EncryptAndSign' + } + ] + } + { + name: 'UpgradeService' + parameters: [ + { + name: 'AppPollIntervalInSeconds' + value: '60' + } + ] + } + ] + location: '' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + maxUnusedVersionsToKeep: 2 + notifications: [ + { + isEnabled: true + notificationCategory: 'WaveProgress' + notificationLevel: 'Critical' + notificationTargets: [ + { + notificationChannel: 'EmailUser' + receivers: [ + 'SomeReceiver' + ] + } + ] + } + ] + roleAssignments: [ + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' + } + ] + tags: { + clusterName: 'sfcmax001' + 'hidden-title': 'This is visible in the resource name' + resourceType: 'Service Fabric' + } + upgradeDescription: { + deltaHealthPolicy: { + maxPercentDeltaUnhealthyApplications: 0 + maxPercentDeltaUnhealthyNodes: 0 + maxPercentUpgradeDomainDeltaUnhealthyNodes: 0 + } + forceRestart: false + healthCheckRetryTimeout: '00:45:00' + healthCheckStableDuration: '00:01:00' + healthCheckWaitDuration: '00:00:30' + healthPolicy: { + maxPercentUnhealthyApplications: 0 + maxPercentUnhealthyNodes: 0 + } + upgradeDomainTimeout: '02:00:00' + upgradeReplicaSetCheckTimeout: '1.00:00:00' + upgradeTimeout: '02:00:00' + } + vmImage: 'Linux' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "managementEndpoint": { + "value": "https://sfcmax001.westeurope.cloudapp.azure.com:19080" + }, + "name": { + "value": "sfcmax001" + }, + "nodeTypes": { + "value": [ + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Silver", + "ephemeralPorts": { + "endPort": 65534, + "startPort": 49152 + }, + "httpGatewayEndpointPort": 19080, + "isPrimary": true, + "isStateless": false, + "multipleAvailabilityZones": false, + "name": "Node01", + "placementProperties": {}, + "reverseProxyEndpointPort": "", + "vmInstanceCount": 5 + }, + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Bronze", + "ephemeralPorts": { + "endPort": 64000, + "httpGatewayEndpointPort": 19007, + "isPrimary": true, + "name": "Node02", + "startPort": 49000, + "vmInstanceCount": 5 + } + } + ] + }, + "reliabilityLevel": { + "value": "Silver" + }, + // Non-required parameters + "addOnFeatures": { + "value": [ + "BackupRestoreService", + "DnsService", + "RepairManager", + "ResourceMonitorService" + ] + }, + "applicationTypes": { + "value": [ + { + "name": "WordCount" + } + ] + }, + "azureActiveDirectory": { + "value": { + "clientApplication": "", + "clusterApplication": "cf33fea8-b30f-424f-ab73-c48d99e0b222", + "tenantId": "" + } + }, + "certificateCommonNames": { + "value": { + "commonNames": [ + { + "certificateCommonName": "certcommon", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130" + } + ], + "x509StoreName": "" + } + }, + "clientCertificateCommonNames": { + "value": [ + { + "certificateCommonName": "clientcommoncert1", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130", + "isAdmin": false + }, + { + "certificateCommonName": "clientcommoncert2", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC131", + "isAdmin": false + } + ] + }, + "clientCertificateThumbprints": { + "value": [ + { + "certificateThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130", + "isAdmin": false + }, + { + "certificateThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC131", + "isAdmin": false + } + ] + }, + "diagnosticsStorageAccountConfig": { + "value": { + "blobEndpoint": "", + "protectedAccountKeyName": "StorageAccountKey1", + "queueEndpoint": "", + "storageAccountName": "", + "tableEndpoint": "" + } + }, + "fabricSettings": { + "value": [ + { + "name": "Security", + "parameters": [ + { + "name": "ClusterProtectionLevel", + "value": "EncryptAndSign" + } + ] + }, + { + "name": "UpgradeService", + "parameters": [ + { + "name": "AppPollIntervalInSeconds", + "value": "60" + } + ] + } + ] + }, + "location": { + "value": "" + }, + "lock": { + "value": { + "kind": "CanNotDelete", + "name": "myCustomLockName" + } + }, + "maxUnusedVersionsToKeep": { + "value": 2 + }, + "notifications": { + "value": [ + { + "isEnabled": true, + "notificationCategory": "WaveProgress", + "notificationLevel": "Critical", + "notificationTargets": [ + { + "notificationChannel": "EmailUser", + "receivers": [ + "SomeReceiver" + ] + } + ] + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Owner" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" + } + ] + }, + "tags": { + "value": { + "clusterName": "sfcmax001", + "hidden-title": "This is visible in the resource name", + "resourceType": "Service Fabric" + } + }, + "upgradeDescription": { + "value": { + "deltaHealthPolicy": { + "maxPercentDeltaUnhealthyApplications": 0, + "maxPercentDeltaUnhealthyNodes": 0, + "maxPercentUpgradeDomainDeltaUnhealthyNodes": 0 + }, + "forceRestart": false, + "healthCheckRetryTimeout": "00:45:00", + "healthCheckStableDuration": "00:01:00", + "healthCheckWaitDuration": "00:00:30", + "healthPolicy": { + "maxPercentUnhealthyApplications": 0, + "maxPercentUnhealthyNodes": 0 + }, + "upgradeDomainTimeout": "02:00:00", + "upgradeReplicaSetCheckTimeout": "1.00:00:00", + "upgradeTimeout": "02:00:00" + } + }, + "vmImage": { + "value": "Linux" + } + } +} +``` + +
+

+ +### Example 4: _WAF-aligned_ + +This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. + + +

+ +via Bicep module + +```bicep +module cluster 'br/public:avm/res/service-fabric/cluster:' = { + name: 'clusterDeployment' + params: { + // Required parameters + managementEndpoint: 'https://sfcwaf001.westeurope.cloudapp.azure.com:19080' + name: 'sfcwaf001' + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Silver' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + isStateless: false + multipleAvailabilityZones: false + name: 'Node01' + placementProperties: {} + reverseProxyEndpointPort: '' + vmInstanceCount: 5 + } + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 64000 + httpGatewayEndpointPort: 19007 + isPrimary: true + name: 'Node02' + startPort: 49000 + vmInstanceCount: 5 + } + } + ] + reliabilityLevel: 'Silver' + // Non-required parameters + addOnFeatures: [ + 'BackupRestoreService' + 'DnsService' + 'RepairManager' + 'ResourceMonitorService' + ] + applicationTypes: [ + { + name: 'WordCount' + } + ] + azureActiveDirectory: { + clientApplication: '' + clusterApplication: 'cf33fea8-b30f-424f-ab73-c48d99e0b222' + tenantId: '' + } + certificateCommonNames: { + commonNames: [ + { + certificateCommonName: 'certcommon' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + } + ] + x509StoreName: '' + } + clientCertificateCommonNames: [ + { + certificateCommonName: 'clientcommoncert1' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateCommonName: 'clientcommoncert2' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + clientCertificateThumbprints: [ + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + diagnosticsStorageAccountConfig: { + blobEndpoint: '' + protectedAccountKeyName: 'StorageAccountKey1' + queueEndpoint: '' + storageAccountName: '' + tableEndpoint: '' + } + fabricSettings: [ + { + name: 'Security' + parameters: [ + { + name: 'ClusterProtectionLevel' + value: 'EncryptAndSign' + } + ] + } + { + name: 'UpgradeService' + parameters: [ + { + name: 'AppPollIntervalInSeconds' + value: '60' + } + ] + } + ] + location: '' + maxUnusedVersionsToKeep: 2 + notifications: [ + { + isEnabled: true + notificationCategory: 'WaveProgress' + notificationLevel: 'Critical' + notificationTargets: [ + { + notificationChannel: 'EmailUser' + receivers: [ + 'SomeReceiver' + ] + } + ] + } + ] + tags: { + clusterName: 'sfcwaf001' + 'hidden-title': 'This is visible in the resource name' + resourceType: 'Service Fabric' + } + upgradeDescription: { + deltaHealthPolicy: { + maxPercentDeltaUnhealthyApplications: 0 + maxPercentDeltaUnhealthyNodes: 0 + maxPercentUpgradeDomainDeltaUnhealthyNodes: 0 + } + forceRestart: false + healthCheckRetryTimeout: '00:45:00' + healthCheckStableDuration: '00:01:00' + healthCheckWaitDuration: '00:00:30' + healthPolicy: { + maxPercentUnhealthyApplications: 0 + maxPercentUnhealthyNodes: 0 + } + upgradeDomainTimeout: '02:00:00' + upgradeReplicaSetCheckTimeout: '1.00:00:00' + upgradeTimeout: '02:00:00' + } + vmImage: 'Linux' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "managementEndpoint": { + "value": "https://sfcwaf001.westeurope.cloudapp.azure.com:19080" + }, + "name": { + "value": "sfcwaf001" + }, + "nodeTypes": { + "value": [ + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Silver", + "ephemeralPorts": { + "endPort": 65534, + "startPort": 49152 + }, + "httpGatewayEndpointPort": 19080, + "isPrimary": true, + "isStateless": false, + "multipleAvailabilityZones": false, + "name": "Node01", + "placementProperties": {}, + "reverseProxyEndpointPort": "", + "vmInstanceCount": 5 + }, + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Bronze", + "ephemeralPorts": { + "endPort": 64000, + "httpGatewayEndpointPort": 19007, + "isPrimary": true, + "name": "Node02", + "startPort": 49000, + "vmInstanceCount": 5 + } + } + ] + }, + "reliabilityLevel": { + "value": "Silver" + }, + // Non-required parameters + "addOnFeatures": { + "value": [ + "BackupRestoreService", + "DnsService", + "RepairManager", + "ResourceMonitorService" + ] + }, + "applicationTypes": { + "value": [ + { + "name": "WordCount" + } + ] + }, + "azureActiveDirectory": { + "value": { + "clientApplication": "", + "clusterApplication": "cf33fea8-b30f-424f-ab73-c48d99e0b222", + "tenantId": "" + } + }, + "certificateCommonNames": { + "value": { + "commonNames": [ + { + "certificateCommonName": "certcommon", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130" + } + ], + "x509StoreName": "" + } + }, + "clientCertificateCommonNames": { + "value": [ + { + "certificateCommonName": "clientcommoncert1", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130", + "isAdmin": false + }, + { + "certificateCommonName": "clientcommoncert2", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC131", + "isAdmin": false + } + ] + }, + "clientCertificateThumbprints": { + "value": [ + { + "certificateThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130", + "isAdmin": false + }, + { + "certificateThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC131", + "isAdmin": false + } + ] + }, + "diagnosticsStorageAccountConfig": { + "value": { + "blobEndpoint": "", + "protectedAccountKeyName": "StorageAccountKey1", + "queueEndpoint": "", + "storageAccountName": "", + "tableEndpoint": "" + } + }, + "fabricSettings": { + "value": [ + { + "name": "Security", + "parameters": [ + { + "name": "ClusterProtectionLevel", + "value": "EncryptAndSign" + } + ] + }, + { + "name": "UpgradeService", + "parameters": [ + { + "name": "AppPollIntervalInSeconds", + "value": "60" + } + ] + } + ] + }, + "location": { + "value": "" + }, + "maxUnusedVersionsToKeep": { + "value": 2 + }, + "notifications": { + "value": [ + { + "isEnabled": true, + "notificationCategory": "WaveProgress", + "notificationLevel": "Critical", + "notificationTargets": [ + { + "notificationChannel": "EmailUser", + "receivers": [ + "SomeReceiver" + ] + } + ] + } + ] + }, + "tags": { + "value": { + "clusterName": "sfcwaf001", + "hidden-title": "This is visible in the resource name", + "resourceType": "Service Fabric" + } + }, + "upgradeDescription": { + "value": { + "deltaHealthPolicy": { + "maxPercentDeltaUnhealthyApplications": 0, + "maxPercentDeltaUnhealthyNodes": 0, + "maxPercentUpgradeDomainDeltaUnhealthyNodes": 0 + }, + "forceRestart": false, + "healthCheckRetryTimeout": "00:45:00", + "healthCheckStableDuration": "00:01:00", + "healthCheckWaitDuration": "00:00:30", + "healthPolicy": { + "maxPercentUnhealthyApplications": 0, + "maxPercentUnhealthyNodes": 0 + }, + "upgradeDomainTimeout": "02:00:00", + "upgradeReplicaSetCheckTimeout": "1.00:00:00", + "upgradeTimeout": "02:00:00" + } + }, + "vmImage": { + "value": "Linux" + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`managementEndpoint`](#parameter-managementendpoint) | string | The http management endpoint of the cluster. | +| [`name`](#parameter-name) | string | Name of the Service Fabric cluster. | +| [`nodeTypes`](#parameter-nodetypes) | array | The list of node types in the cluster. | +| [`reliabilityLevel`](#parameter-reliabilitylevel) | string | The reliability level sets the replica set size of system services. Learn about ReliabilityLevel (https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-cluster-capacity). - None - Run the System services with a target replica set count of 1. This should only be used for test clusters. - Bronze - Run the System services with a target replica set count of 3. This should only be used for test clusters. - Silver - Run the System services with a target replica set count of 5. - Gold - Run the System services with a target replica set count of 7. - Platinum - Run the System services with a target replica set count of 9. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`addOnFeatures`](#parameter-addonfeatures) | array | The list of add-on features to enable in the cluster. | +| [`applicationTypes`](#parameter-applicationtypes) | array | Array of Service Fabric cluster application types. | +| [`azureActiveDirectory`](#parameter-azureactivedirectory) | object | The settings to enable AAD authentication on the cluster. | +| [`certificate`](#parameter-certificate) | object | Describes the certificate details like thumbprint of the primary certificate, thumbprint of the secondary certificate and the local certificate store location. | +| [`certificateCommonNames`](#parameter-certificatecommonnames) | object | Describes a list of server certificates referenced by common name that are used to secure the cluster. | +| [`clientCertificateCommonNames`](#parameter-clientcertificatecommonnames) | array | The list of client certificates referenced by common name that are allowed to manage the cluster. | +| [`clientCertificateThumbprints`](#parameter-clientcertificatethumbprints) | array | The list of client certificates referenced by thumbprint that are allowed to manage the cluster. | +| [`clusterCodeVersion`](#parameter-clustercodeversion) | string | The Service Fabric runtime version of the cluster. This property can only by set the user when upgradeMode is set to "Manual". To get list of available Service Fabric versions for new clusters use ClusterVersion API. To get the list of available version for existing clusters use availableClusterVersions. | +| [`diagnosticsStorageAccountConfig`](#parameter-diagnosticsstorageaccountconfig) | object | The storage account information for storing Service Fabric diagnostic logs. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`eventStoreServiceEnabled`](#parameter-eventstoreserviceenabled) | bool | Indicates if the event store service is enabled. | +| [`fabricSettings`](#parameter-fabricsettings) | array | The list of custom fabric settings to configure the cluster. | +| [`infrastructureServiceManager`](#parameter-infrastructureservicemanager) | bool | Indicates if infrastructure service manager is enabled. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`maxUnusedVersionsToKeep`](#parameter-maxunusedversionstokeep) | int | Number of unused versions per application type to keep. | +| [`notifications`](#parameter-notifications) | array | Indicates a list of notification channels for cluster events. | +| [`reverseProxyCertificate`](#parameter-reverseproxycertificate) | object | Describes the certificate details. | +| [`reverseProxyCertificateCommonNames`](#parameter-reverseproxycertificatecommonnames) | object | Describes a list of server certificates referenced by common name that are used to secure the cluster. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`sfZonalUpgradeMode`](#parameter-sfzonalupgrademode) | string | This property controls the logical grouping of VMs in upgrade domains (UDs). This property cannot be modified if a node type with multiple Availability Zones is already present in the cluster. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`upgradeDescription`](#parameter-upgradedescription) | object | Describes the policy used when upgrading the cluster. | +| [`upgradeMode`](#parameter-upgrademode) | string | The upgrade mode of the cluster when new Service Fabric runtime version is available. | +| [`upgradePauseEndTimestampUtc`](#parameter-upgradepauseendtimestamputc) | string | Indicates the end date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC). | +| [`upgradePauseStartTimestampUtc`](#parameter-upgradepausestarttimestamputc) | string | Indicates the start date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC). | +| [`upgradeWave`](#parameter-upgradewave) | string | Indicates when new cluster runtime version upgrades will be applied after they are released. By default is Wave0. | +| [`vmImage`](#parameter-vmimage) | string | The VM image VMSS has been configured with. Generic names such as Windows or Linux can be used. | +| [`vmssZonalUpgradeMode`](#parameter-vmsszonalupgrademode) | string | This property defines the upgrade mode for the virtual machine scale set, it is mandatory if a node type with multiple Availability Zones is added. | +| [`waveUpgradePaused`](#parameter-waveupgradepaused) | bool | Boolean to pause automatic runtime version upgrades to the cluster. | + +### Parameter: `managementEndpoint` + +The http management endpoint of the cluster. + +- Required: Yes +- Type: string + +### Parameter: `name` + +Name of the Service Fabric cluster. + +- Required: Yes +- Type: string + +### Parameter: `nodeTypes` + +The list of node types in the cluster. + +- Required: Yes +- Type: array + +### Parameter: `reliabilityLevel` + +The reliability level sets the replica set size of system services. Learn about ReliabilityLevel (https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-cluster-capacity). - None - Run the System services with a target replica set count of 1. This should only be used for test clusters. - Bronze - Run the System services with a target replica set count of 3. This should only be used for test clusters. - Silver - Run the System services with a target replica set count of 5. - Gold - Run the System services with a target replica set count of 7. - Platinum - Run the System services with a target replica set count of 9. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Bronze' + 'Gold' + 'None' + 'Platinum' + 'Silver' + ] + ``` + +### Parameter: `addOnFeatures` + +The list of add-on features to enable in the cluster. + +- Required: No +- Type: array +- Default: `[]` +- Allowed: + ```Bicep + [ + 'BackupRestoreService' + 'DnsService' + 'RepairManager' + 'ResourceMonitorService' + ] + ``` + +### Parameter: `applicationTypes` + +Array of Service Fabric cluster application types. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `azureActiveDirectory` + +The settings to enable AAD authentication on the cluster. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `certificate` + +Describes the certificate details like thumbprint of the primary certificate, thumbprint of the secondary certificate and the local certificate store location. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `certificateCommonNames` + +Describes a list of server certificates referenced by common name that are used to secure the cluster. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `clientCertificateCommonNames` + +The list of client certificates referenced by common name that are allowed to manage the cluster. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `clientCertificateThumbprints` + +The list of client certificates referenced by thumbprint that are allowed to manage the cluster. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `clusterCodeVersion` + +The Service Fabric runtime version of the cluster. This property can only by set the user when upgradeMode is set to "Manual". To get list of available Service Fabric versions for new clusters use ClusterVersion API. To get the list of available version for existing clusters use availableClusterVersions. + +- Required: No +- Type: string + +### Parameter: `diagnosticsStorageAccountConfig` + +The storage account information for storing Service Fabric diagnostic logs. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `eventStoreServiceEnabled` + +Indicates if the event store service is enabled. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `fabricSettings` + +The list of custom fabric settings to configure the cluster. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `infrastructureServiceManager` + +Indicates if infrastructure service manager is enabled. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `maxUnusedVersionsToKeep` + +Number of unused versions per application type to keep. + +- Required: No +- Type: int +- Default: `3` + +### Parameter: `notifications` + +Indicates a list of notification channels for cluster events. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `reverseProxyCertificate` + +Describes the certificate details. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `reverseProxyCertificateCommonNames` + +Describes a list of server certificates referenced by common name that are used to secure the cluster. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". | +| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `sfZonalUpgradeMode` + +This property controls the logical grouping of VMs in upgrade domains (UDs). This property cannot be modified if a node type with multiple Availability Zones is already present in the cluster. + +- Required: No +- Type: string +- Default: `'Hierarchical'` +- Allowed: + ```Bicep + [ + 'Hierarchical' + 'Parallel' + ] + ``` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + +### Parameter: `upgradeDescription` + +Describes the policy used when upgrading the cluster. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `upgradeMode` + +The upgrade mode of the cluster when new Service Fabric runtime version is available. + +- Required: No +- Type: string +- Default: `'Automatic'` +- Allowed: + ```Bicep + [ + 'Automatic' + 'Manual' + ] + ``` + +### Parameter: `upgradePauseEndTimestampUtc` + +Indicates the end date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC). + +- Required: No +- Type: string + +### Parameter: `upgradePauseStartTimestampUtc` + +Indicates the start date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC). + +- Required: No +- Type: string + +### Parameter: `upgradeWave` + +Indicates when new cluster runtime version upgrades will be applied after they are released. By default is Wave0. + +- Required: No +- Type: string +- Default: `'Wave0'` +- Allowed: + ```Bicep + [ + 'Wave0' + 'Wave1' + 'Wave2' + ] + ``` + +### Parameter: `vmImage` + +The VM image VMSS has been configured with. Generic names such as Windows or Linux can be used. + +- Required: No +- Type: string + +### Parameter: `vmssZonalUpgradeMode` + +This property defines the upgrade mode for the virtual machine scale set, it is mandatory if a node type with multiple Availability Zones is added. + +- Required: No +- Type: string +- Default: `'Hierarchical'` +- Allowed: + ```Bicep + [ + 'Hierarchical' + 'Parallel' + ] + ``` + +### Parameter: `waveUpgradePaused` + +Boolean to pause automatic runtime version upgrades to the cluster. + +- Required: No +- Type: bool +- Default: `False` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `endpoint` | string | The Service Fabric Cluster endpoint. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The Service Fabric Cluster name. | +| `resourceGroupName` | string | The Service Fabric Cluster resource group. | +| `resourceId` | string | The Service Fabric Cluster resource ID. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/service-fabric/cluster/application-type/README.md b/avm/res/service-fabric/cluster/application-type/README.md new file mode 100644 index 0000000000..c9d50e5414 --- /dev/null +++ b/avm/res/service-fabric/cluster/application-type/README.md @@ -0,0 +1,71 @@ +# Service Fabric Cluster Application Types `[Microsoft.ServiceFabric/clusters/applicationTypes]` + +This module deploys a Service Fabric Cluster Application Type. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ServiceFabric/clusters/applicationTypes` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ServiceFabric/2021-06-01/clusters/applicationTypes) | + +## Parameters + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`serviceFabricClusterName`](#parameter-servicefabricclustername) | string | The name of the parent Service Fabric cluster. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Application type name. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | + +### Parameter: `serviceFabricClusterName` + +The name of the parent Service Fabric cluster. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `name` + +Application type name. + +- Required: No +- Type: string +- Default: `'defaultApplicationType'` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The resource name of the Application type. | +| `resourceGroupName` | string | The resource group of the Application type. | +| `resourceID` | string | The resource ID of the Application type. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/service-fabric/cluster/application-type/main.bicep b/avm/res/service-fabric/cluster/application-type/main.bicep new file mode 100644 index 0000000000..63dec4bc25 --- /dev/null +++ b/avm/res/service-fabric/cluster/application-type/main.bicep @@ -0,0 +1,31 @@ +metadata name = 'Service Fabric Cluster Application Types' +metadata description = 'This module deploys a Service Fabric Cluster Application Type.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent Service Fabric cluster. Required if the template is used in a standalone deployment.') +param serviceFabricClusterName string + +@description('Optional. Application type name.') +param name string = 'defaultApplicationType' + +@description('Optional. Tags of the resource.') +param tags object? + +resource serviceFabricCluster 'Microsoft.ServiceFabric/clusters@2021-06-01' existing = { + name: serviceFabricClusterName +} + +resource applicationTypes 'Microsoft.ServiceFabric/clusters/applicationTypes@2021-06-01' = { + name: name + parent: serviceFabricCluster + tags: tags +} + +@description('The resource name of the Application type.') +output name string = applicationTypes.name + +@description('The resource group of the Application type.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the Application type.') +output resourceID string = applicationTypes.id diff --git a/avm/res/service-fabric/cluster/application-type/main.json b/avm/res/service-fabric/cluster/application-type/main.json new file mode 100644 index 0000000000..0e9d6a25b4 --- /dev/null +++ b/avm/res/service-fabric/cluster/application-type/main.json @@ -0,0 +1,77 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "4241007545422374207" + }, + "name": "Service Fabric Cluster Application Types", + "description": "This module deploys a Service Fabric Cluster Application Type.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "serviceFabricClusterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Service Fabric cluster. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "defaultApplicationType", + "metadata": { + "description": "Optional. Application type name." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "serviceFabricCluster": { + "existing": true, + "type": "Microsoft.ServiceFabric/clusters", + "apiVersion": "2021-06-01", + "name": "[parameters('serviceFabricClusterName')]" + }, + "applicationTypes": { + "type": "Microsoft.ServiceFabric/clusters/applicationTypes", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}', parameters('serviceFabricClusterName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "dependsOn": [ + "serviceFabricCluster" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The resource name of the Application type." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the Application type." + }, + "value": "[resourceGroup().name]" + }, + "resourceID": { + "type": "string", + "metadata": { + "description": "The resource ID of the Application type." + }, + "value": "[resourceId('Microsoft.ServiceFabric/clusters/applicationTypes', parameters('serviceFabricClusterName'), parameters('name'))]" + } + } +} \ No newline at end of file diff --git a/avm/res/service-fabric/cluster/main.bicep b/avm/res/service-fabric/cluster/main.bicep new file mode 100644 index 0000000000..d2bcd4e614 --- /dev/null +++ b/avm/res/service-fabric/cluster/main.bicep @@ -0,0 +1,479 @@ +metadata name = 'Service Fabric Clusters' +metadata description = 'This module deploys a Service Fabric Cluster.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. Name of the Service Fabric cluster.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Optional. The lock settings of the service.') +param lock lockType + +@allowed([ + 'BackupRestoreService' + 'DnsService' + 'RepairManager' + 'ResourceMonitorService' +]) +@description('Optional. The list of add-on features to enable in the cluster.') +param addOnFeatures array = [] + +@description('Optional. Number of unused versions per application type to keep.') +param maxUnusedVersionsToKeep int = 3 + +@description('Optional. The settings to enable AAD authentication on the cluster.') +param azureActiveDirectory object = {} + +@description('Optional. Describes the certificate details like thumbprint of the primary certificate, thumbprint of the secondary certificate and the local certificate store location.') +param certificate object = {} + +@description('Optional. Describes a list of server certificates referenced by common name that are used to secure the cluster.') +param certificateCommonNames object = {} + +@description('Optional. The list of client certificates referenced by common name that are allowed to manage the cluster.') +param clientCertificateCommonNames array = [] + +@description('Optional. The list of client certificates referenced by thumbprint that are allowed to manage the cluster.') +param clientCertificateThumbprints array = [] + +@description('Optional. The Service Fabric runtime version of the cluster. This property can only by set the user when upgradeMode is set to "Manual". To get list of available Service Fabric versions for new clusters use ClusterVersion API. To get the list of available version for existing clusters use availableClusterVersions.') +param clusterCodeVersion string? + +@description('Optional. The storage account information for storing Service Fabric diagnostic logs.') +param diagnosticsStorageAccountConfig object = {} + +@description('Optional. Indicates if the event store service is enabled.') +param eventStoreServiceEnabled bool = false + +@description('Optional. The list of custom fabric settings to configure the cluster.') +param fabricSettings array = [] + +@description('Optional. Indicates if infrastructure service manager is enabled.') +param infrastructureServiceManager bool = false + +@description('Required. The http management endpoint of the cluster.') +param managementEndpoint string + +@description('Required. The list of node types in the cluster.') +param nodeTypes array + +@description('Optional. Indicates a list of notification channels for cluster events.') +param notifications array = [] + +@allowed([ + 'Bronze' + 'Gold' + 'None' + 'Platinum' + 'Silver' +]) +@description('Required. The reliability level sets the replica set size of system services. Learn about ReliabilityLevel (https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-cluster-capacity). - None - Run the System services with a target replica set count of 1. This should only be used for test clusters. - Bronze - Run the System services with a target replica set count of 3. This should only be used for test clusters. - Silver - Run the System services with a target replica set count of 5. - Gold - Run the System services with a target replica set count of 7. - Platinum - Run the System services with a target replica set count of 9.') +param reliabilityLevel string + +@description('Optional. Describes the certificate details.') +param reverseProxyCertificate object = {} + +@description('Optional. Describes a list of server certificates referenced by common name that are used to secure the cluster.') +param reverseProxyCertificateCommonNames object = {} + +@allowed([ + 'Hierarchical' + 'Parallel' +]) +@description('Optional. This property controls the logical grouping of VMs in upgrade domains (UDs). This property cannot be modified if a node type with multiple Availability Zones is already present in the cluster.') +param sfZonalUpgradeMode string = 'Hierarchical' + +@description('Optional. Describes the policy used when upgrading the cluster.') +param upgradeDescription object = {} + +@allowed([ + 'Automatic' + 'Manual' +]) +@description('Optional. The upgrade mode of the cluster when new Service Fabric runtime version is available.') +param upgradeMode string = 'Automatic' + +@description('Optional. Indicates the end date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC).') +param upgradePauseEndTimestampUtc string? + +@description('Optional. Indicates the start date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC).') +param upgradePauseStartTimestampUtc string? + +@allowed([ + 'Wave0' + 'Wave1' + 'Wave2' +]) +@description('Optional. Indicates when new cluster runtime version upgrades will be applied after they are released. By default is Wave0.') +param upgradeWave string = 'Wave0' + +@description('Optional. The VM image VMSS has been configured with. Generic names such as Windows or Linux can be used.') +param vmImage string? + +@allowed([ + 'Hierarchical' + 'Parallel' +]) +@description('Optional. This property defines the upgrade mode for the virtual machine scale set, it is mandatory if a node type with multiple Availability Zones is added.') +param vmssZonalUpgradeMode string = 'Hierarchical' + +@description('Optional. Boolean to pause automatic runtime version upgrades to the cluster.') +param waveUpgradePaused bool = false + +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType + +@description('Optional. Array of Service Fabric cluster application types.') +param applicationTypes array = [] + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +var clientCertificateCommonNamesVar = [ + for clientCertificateCommonName in clientCertificateCommonNames: { + certificateCommonName: contains(clientCertificateCommonName, 'certificateCommonName') + ? clientCertificateCommonName.certificateCommonName + : null + certificateIssuerThumbprint: contains(clientCertificateCommonName, 'certificateIssuerThumbprint') + ? clientCertificateCommonName.certificateIssuerThumbprint + : null + isAdmin: contains(clientCertificateCommonName, 'isAdmin') ? clientCertificateCommonName.isAdmin : false + } +] + +var clientCertificateThumbprintsVar = [ + for clientCertificateThumbprint in clientCertificateThumbprints: { + certificateThumbprint: contains(clientCertificateThumbprint, 'certificateThumbprint') + ? clientCertificateThumbprint.certificateThumbprint + : null + isAdmin: contains(clientCertificateThumbprint, 'isAdmin') ? clientCertificateThumbprint.isAdmin : false + } +] + +var fabricSettingsVar = [ + for fabricSetting in fabricSettings: { + name: contains(fabricSetting, 'name') ? fabricSetting.name : null + parameters: contains(fabricSetting, 'parameters') ? fabricSetting.parameters : null + } +] + +var fnodeTypesVar = [ + for nodeType in nodeTypes: { + applicationPorts: contains(nodeType, 'applicationPorts') + ? { + endPort: contains(nodeType.applicationPorts, 'endPort') ? nodeType.applicationPorts.endPort : null + startPort: contains(nodeType.applicationPorts, 'startPort') ? nodeType.applicationPorts.startPort : null + } + : null + capacities: contains(nodeType, 'capacities') ? nodeType.capacities : null + clientConnectionEndpointPort: contains(nodeType, 'clientConnectionEndpointPort') + ? nodeType.clientConnectionEndpointPort + : null + durabilityLevel: contains(nodeType, 'durabilityLevel') ? nodeType.durabilityLevel : null + ephemeralPorts: contains(nodeType, 'ephemeralPorts') + ? { + endPort: contains(nodeType.ephemeralPorts, 'endPort') ? nodeType.ephemeralPorts.endPort : null + startPort: contains(nodeType.ephemeralPorts, 'startPort') ? nodeType.ephemeralPorts.startPort : null + } + : null + httpGatewayEndpointPort: contains(nodeType, 'httpGatewayEndpointPort') ? nodeType.httpGatewayEndpointPort : null + isPrimary: contains(nodeType, 'isPrimary') ? nodeType.isPrimary : null + isStateless: contains(nodeType, 'isStateless') ? nodeType.isStateless : null + multipleAvailabilityZones: contains(nodeType, 'multipleAvailabilityZones') + ? nodeType.multipleAvailabilityZones + : null + name: contains(nodeType, 'name') ? nodeType.name : 'Node00' + placementProperties: contains(nodeType, 'placementProperties') ? nodeType.placementProperties : null + reverseProxyEndpointPort: contains(nodeType, 'reverseProxyEndpointPort') ? nodeType.reverseProxyEndpointPort : null + vmInstanceCount: contains(nodeType, 'vmInstanceCount') ? nodeType.vmInstanceCount : 1 + } +] + +var notificationsVar = [ + for notification in notifications: { + isEnabled: contains(notification, 'isEnabled') ? notification.isEnabled : false + notificationCategory: contains(notification, 'notificationCategory') + ? notification.notificationCategory + : 'WaveProgress' + notificationLevel: contains(notification, 'notificationLevel') ? notification.notificationLevel : 'All' + notificationTargets: contains(notification, 'notificationTargets') ? notification.notificationTargets : [] + } +] + +var upgradeDescriptionVar = union( + { + deltaHealthPolicy: { + applicationDeltaHealthPolicies: upgradeDescription.?applicationDeltaHealthPolicies ?? {} + maxPercentDeltaUnhealthyApplications: upgradeDescription.?maxPercentDeltaUnhealthyApplications ?? 0 + maxPercentDeltaUnhealthyNodes: upgradeDescription.?maxPercentDeltaUnhealthyNodes ?? 0 + maxPercentUpgradeDomainDeltaUnhealthyNodes: upgradeDescription.?maxPercentUpgradeDomainDeltaUnhealthyNodes ?? 0 + } + forceRestart: contains(upgradeDescription, 'forceRestart') ? upgradeDescription.forceRestart : false + healthCheckRetryTimeout: contains(upgradeDescription, 'healthCheckRetryTimeout') + ? upgradeDescription.healthCheckRetryTimeout + : '00:45:00' + healthCheckStableDuration: contains(upgradeDescription, 'healthCheckStableDuration') + ? upgradeDescription.healthCheckStableDuration + : '00:01:00' + healthCheckWaitDuration: contains(upgradeDescription, 'healthCheckWaitDuration') + ? upgradeDescription.healthCheckWaitDuration + : '00:00:30' + upgradeDomainTimeout: contains(upgradeDescription, 'upgradeDomainTimeout') + ? upgradeDescription.upgradeDomainTimeout + : '02:00:00' + upgradeReplicaSetCheckTimeout: contains(upgradeDescription, 'upgradeReplicaSetCheckTimeout') + ? upgradeDescription.upgradeReplicaSetCheckTimeout + : '1.00:00:00' + upgradeTimeout: contains(upgradeDescription, 'upgradeTimeout') ? upgradeDescription.upgradeTimeout : '02:00:00' + }, + contains(upgradeDescription, 'healthPolicy') + ? { + healthPolicy: { + applicationHealthPolicies: contains(upgradeDescription.healthPolicy, 'applicationHealthPolicies') + ? upgradeDescription.healthPolicy.applicationHealthPolicies + : {} + maxPercentUnhealthyApplications: contains(upgradeDescription.healthPolicy, 'maxPercentUnhealthyApplications') + ? upgradeDescription.healthPolicy.maxPercentUnhealthyApplications + : 0 + maxPercentUnhealthyNodes: contains(upgradeDescription.healthPolicy, 'maxPercentUnhealthyNodes') + ? upgradeDescription.healthPolicy.maxPercentUnhealthyNodes + : 0 + } + } + : {} +) + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) +} + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = + if (enableTelemetry) { + name: '46d3xbcp.res.servicefabric-cluster.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } + } + +// Service Fabric cluster resource +resource serviceFabricCluster 'Microsoft.ServiceFabric/clusters@2021-06-01' = { + name: name + location: location + tags: tags + properties: { + addOnFeatures: addOnFeatures + applicationTypeVersionsCleanupPolicy: { + maxUnusedVersionsToKeep: maxUnusedVersionsToKeep + } + azureActiveDirectory: !empty(azureActiveDirectory) + ? { + clientApplication: contains(azureActiveDirectory, 'clientApplication') + ? azureActiveDirectory.clientApplication + : null + clusterApplication: contains(azureActiveDirectory, 'clusterApplication') + ? azureActiveDirectory.clusterApplication + : null + tenantId: contains(azureActiveDirectory, 'tenantId') ? azureActiveDirectory.tenantId : null + } + : null + certificate: !empty(certificate) + ? { + thumbprint: contains(certificate, 'thumbprint') ? certificate.thumbprint : null + thumbprintSecondary: contains(certificate, 'thumbprintSecondary') ? certificate.thumbprintSecondary : null + x509StoreName: contains(certificate, 'x509StoreName') ? certificate.x509StoreName : null + } + : null + certificateCommonNames: !empty(certificateCommonNames) + ? { + commonNames: contains(certificateCommonNames, 'commonNames') ? certificateCommonNames.commonNames : null + x509StoreName: contains(certificateCommonNames, 'certificateCommonNamesx509StoreName') + ? certificateCommonNames.certificateCommonNamesx509StoreName + : null + } + : null + clientCertificateCommonNames: !empty(clientCertificateCommonNames) ? clientCertificateCommonNamesVar : null + clientCertificateThumbprints: !empty(clientCertificateThumbprints) ? clientCertificateThumbprintsVar : null + clusterCodeVersion: clusterCodeVersion + diagnosticsStorageAccountConfig: !empty(diagnosticsStorageAccountConfig) + ? { + blobEndpoint: contains(diagnosticsStorageAccountConfig, 'blobEndpoint') + ? diagnosticsStorageAccountConfig.blobEndpoint + : null + protectedAccountKeyName: contains(diagnosticsStorageAccountConfig, 'protectedAccountKeyName') + ? diagnosticsStorageAccountConfig.protectedAccountKeyName + : null + protectedAccountKeyName2: contains(diagnosticsStorageAccountConfig, 'protectedAccountKeyName2') + ? diagnosticsStorageAccountConfig.protectedAccountKeyName2 + : null + queueEndpoint: contains(diagnosticsStorageAccountConfig, 'queueEndpoint') + ? diagnosticsStorageAccountConfig.queueEndpoint + : null + storageAccountName: contains(diagnosticsStorageAccountConfig, 'storageAccountName') + ? diagnosticsStorageAccountConfig.storageAccountName + : null + tableEndpoint: contains(diagnosticsStorageAccountConfig, 'tableEndpoint') + ? diagnosticsStorageAccountConfig.tableEndpoint + : null + } + : null + eventStoreServiceEnabled: eventStoreServiceEnabled + fabricSettings: !empty(fabricSettings) ? fabricSettingsVar : null + infrastructureServiceManager: infrastructureServiceManager + managementEndpoint: managementEndpoint + nodeTypes: !empty(nodeTypes) ? fnodeTypesVar : [] + notifications: !empty(notifications) ? notificationsVar : null + reliabilityLevel: !empty(reliabilityLevel) ? reliabilityLevel : 'None' + reverseProxyCertificate: !empty(reverseProxyCertificate) + ? { + thumbprint: contains(reverseProxyCertificate, 'thumbprint') ? reverseProxyCertificate.thumbprint : null + thumbprintSecondary: contains(reverseProxyCertificate, 'thumbprintSecondary') + ? reverseProxyCertificate.thumbprintSecondary + : null + x509StoreName: contains(reverseProxyCertificate, 'x509StoreName') + ? reverseProxyCertificate.x509StoreName + : null + } + : null + reverseProxyCertificateCommonNames: !empty(reverseProxyCertificateCommonNames) + ? { + commonNames: contains(reverseProxyCertificateCommonNames, 'commonNames') + ? reverseProxyCertificateCommonNames.commonNames + : null + x509StoreName: contains(reverseProxyCertificateCommonNames, 'x509StoreName') + ? reverseProxyCertificateCommonNames.x509StoreName + : null + } + : null + sfZonalUpgradeMode: !empty(sfZonalUpgradeMode) ? sfZonalUpgradeMode : null + upgradeDescription: !empty(upgradeDescription) ? upgradeDescriptionVar : null + upgradeMode: !empty(upgradeMode) ? upgradeMode : null + upgradePauseEndTimestampUtc: upgradePauseEndTimestampUtc + upgradePauseStartTimestampUtc: upgradePauseStartTimestampUtc + upgradeWave: !empty(upgradeWave) ? upgradeWave : null + vmImage: vmImage + vmssZonalUpgradeMode: !empty(vmssZonalUpgradeMode) ? vmssZonalUpgradeMode : null + waveUpgradePaused: waveUpgradePaused + } +} + +// Service Fabric cluster resource lock +resource serviceFabricCluster_lock 'Microsoft.Authorization/locks@2020-05-01' = + if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' + ? 'Cannot delete resource or child resources.' + : 'Cannot delete or modify the resource or child resources.' + } + scope: serviceFabricCluster + } + +// Service Fabric cluster RBAC assignment +resource serviceFabricCluster_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (roleAssignments ?? []): { + name: guid(serviceFabricCluster.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) + ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] + : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: serviceFabricCluster + } +] + +// Service Fabric cluster application types +module serviceFabricCluster_applicationTypes 'application-type/main.bicep' = [ + for applicationType in applicationTypes: { + name: '${uniqueString(deployment().name, location)}-SFC-${applicationType.name}' + params: { + name: applicationType.name + serviceFabricClusterName: serviceFabricCluster.name + tags: applicationType.?tags ?? tags + } + } +] + +@description('The Service Fabric Cluster name.') +output name string = serviceFabricCluster.name + +@description('The Service Fabric Cluster resource group.') +output resourceGroupName string = resourceGroup().name + +@description('The Service Fabric Cluster resource ID.') +output resourceId string = serviceFabricCluster.id + +@description('The Service Fabric Cluster endpoint.') +output endpoint string = serviceFabricCluster.properties.clusterEndpoint + +@description('The location the resource was deployed into.') +output location string = serviceFabricCluster.location + +// =============== // +// Definitions // +// =============== // + +type lockType = { + @description('Optional. Specify the name of lock.') + name: string? + + @description('Optional. Specify the type of lock.') + kind: ('CanNotDelete' | 'ReadOnly' | 'None')? +}? + +type roleAssignmentType = { + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') + roleDefinitionIdOrName: string + + @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') + principalId: string + + @description('Optional. The principal type of the assigned principal ID.') + principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')? + + @description('Optional. The description of the role assignment.') + description: string? + + @description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') + condition: string? + + @description('Optional. Version of the condition.') + conditionVersion: '2.0'? + + @description('Optional. The Resource Id of the delegated managed identity resource.') + delegatedManagedIdentityResourceId: string? +}[]? diff --git a/avm/res/service-fabric/cluster/main.json b/avm/res/service-fabric/cluster/main.json new file mode 100644 index 0000000000..07ec9d6764 --- /dev/null +++ b/avm/res/service-fabric/cluster/main.json @@ -0,0 +1,677 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "2708589601167951653" + }, + "name": "Service Fabric Clusters", + "description": "This module deploys a Service Fabric Cluster.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Service Fabric cluster." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "addOnFeatures": { + "type": "array", + "defaultValue": [], + "allowedValues": [ + "BackupRestoreService", + "DnsService", + "RepairManager", + "ResourceMonitorService" + ], + "metadata": { + "description": "Optional. The list of add-on features to enable in the cluster." + } + }, + "maxUnusedVersionsToKeep": { + "type": "int", + "defaultValue": 3, + "metadata": { + "description": "Optional. Number of unused versions per application type to keep." + } + }, + "azureActiveDirectory": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The settings to enable AAD authentication on the cluster." + } + }, + "certificate": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Describes the certificate details like thumbprint of the primary certificate, thumbprint of the secondary certificate and the local certificate store location." + } + }, + "certificateCommonNames": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Describes a list of server certificates referenced by common name that are used to secure the cluster." + } + }, + "clientCertificateCommonNames": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of client certificates referenced by common name that are allowed to manage the cluster." + } + }, + "clientCertificateThumbprints": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of client certificates referenced by thumbprint that are allowed to manage the cluster." + } + }, + "clusterCodeVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Service Fabric runtime version of the cluster. This property can only by set the user when upgradeMode is set to \"Manual\". To get list of available Service Fabric versions for new clusters use ClusterVersion API. To get the list of available version for existing clusters use availableClusterVersions." + } + }, + "diagnosticsStorageAccountConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The storage account information for storing Service Fabric diagnostic logs." + } + }, + "eventStoreServiceEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates if the event store service is enabled." + } + }, + "fabricSettings": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of custom fabric settings to configure the cluster." + } + }, + "infrastructureServiceManager": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates if infrastructure service manager is enabled." + } + }, + "managementEndpoint": { + "type": "string", + "metadata": { + "description": "Required. The http management endpoint of the cluster." + } + }, + "nodeTypes": { + "type": "array", + "metadata": { + "description": "Required. The list of node types in the cluster." + } + }, + "notifications": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Indicates a list of notification channels for cluster events." + } + }, + "reliabilityLevel": { + "type": "string", + "allowedValues": [ + "Bronze", + "Gold", + "None", + "Platinum", + "Silver" + ], + "metadata": { + "description": "Required. The reliability level sets the replica set size of system services. Learn about ReliabilityLevel (https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-cluster-capacity). - None - Run the System services with a target replica set count of 1. This should only be used for test clusters. - Bronze - Run the System services with a target replica set count of 3. This should only be used for test clusters. - Silver - Run the System services with a target replica set count of 5. - Gold - Run the System services with a target replica set count of 7. - Platinum - Run the System services with a target replica set count of 9." + } + }, + "reverseProxyCertificate": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Describes the certificate details." + } + }, + "reverseProxyCertificateCommonNames": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Describes a list of server certificates referenced by common name that are used to secure the cluster." + } + }, + "sfZonalUpgradeMode": { + "type": "string", + "defaultValue": "Hierarchical", + "allowedValues": [ + "Hierarchical", + "Parallel" + ], + "metadata": { + "description": "Optional. This property controls the logical grouping of VMs in upgrade domains (UDs). This property cannot be modified if a node type with multiple Availability Zones is already present in the cluster." + } + }, + "upgradeDescription": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Describes the policy used when upgrading the cluster." + } + }, + "upgradeMode": { + "type": "string", + "defaultValue": "Automatic", + "allowedValues": [ + "Automatic", + "Manual" + ], + "metadata": { + "description": "Optional. The upgrade mode of the cluster when new Service Fabric runtime version is available." + } + }, + "upgradePauseEndTimestampUtc": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Indicates the end date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC)." + } + }, + "upgradePauseStartTimestampUtc": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Indicates the start date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC)." + } + }, + "upgradeWave": { + "type": "string", + "defaultValue": "Wave0", + "allowedValues": [ + "Wave0", + "Wave1", + "Wave2" + ], + "metadata": { + "description": "Optional. Indicates when new cluster runtime version upgrades will be applied after they are released. By default is Wave0." + } + }, + "vmImage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The VM image VMSS has been configured with. Generic names such as Windows or Linux can be used." + } + }, + "vmssZonalUpgradeMode": { + "type": "string", + "defaultValue": "Hierarchical", + "allowedValues": [ + "Hierarchical", + "Parallel" + ], + "metadata": { + "description": "Optional. This property defines the upgrade mode for the virtual machine scale set, it is mandatory if a node type with multiple Availability Zones is added." + } + }, + "waveUpgradePaused": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Boolean to pause automatic runtime version upgrades to the cluster." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "applicationTypes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of Service Fabric cluster application types." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "clientCertificateCommonNamesVar", + "count": "[length(parameters('clientCertificateCommonNames'))]", + "input": { + "certificateCommonName": "[if(contains(parameters('clientCertificateCommonNames')[copyIndex('clientCertificateCommonNamesVar')], 'certificateCommonName'), parameters('clientCertificateCommonNames')[copyIndex('clientCertificateCommonNamesVar')].certificateCommonName, null())]", + "certificateIssuerThumbprint": "[if(contains(parameters('clientCertificateCommonNames')[copyIndex('clientCertificateCommonNamesVar')], 'certificateIssuerThumbprint'), parameters('clientCertificateCommonNames')[copyIndex('clientCertificateCommonNamesVar')].certificateIssuerThumbprint, null())]", + "isAdmin": "[if(contains(parameters('clientCertificateCommonNames')[copyIndex('clientCertificateCommonNamesVar')], 'isAdmin'), parameters('clientCertificateCommonNames')[copyIndex('clientCertificateCommonNamesVar')].isAdmin, false())]" + } + }, + { + "name": "clientCertificateThumbprintsVar", + "count": "[length(parameters('clientCertificateThumbprints'))]", + "input": { + "certificateThumbprint": "[if(contains(parameters('clientCertificateThumbprints')[copyIndex('clientCertificateThumbprintsVar')], 'certificateThumbprint'), parameters('clientCertificateThumbprints')[copyIndex('clientCertificateThumbprintsVar')].certificateThumbprint, null())]", + "isAdmin": "[if(contains(parameters('clientCertificateThumbprints')[copyIndex('clientCertificateThumbprintsVar')], 'isAdmin'), parameters('clientCertificateThumbprints')[copyIndex('clientCertificateThumbprintsVar')].isAdmin, false())]" + } + }, + { + "name": "fabricSettingsVar", + "count": "[length(parameters('fabricSettings'))]", + "input": { + "name": "[if(contains(parameters('fabricSettings')[copyIndex('fabricSettingsVar')], 'name'), parameters('fabricSettings')[copyIndex('fabricSettingsVar')].name, null())]", + "parameters": "[if(contains(parameters('fabricSettings')[copyIndex('fabricSettingsVar')], 'parameters'), parameters('fabricSettings')[copyIndex('fabricSettingsVar')].parameters, null())]" + } + }, + { + "name": "fnodeTypesVar", + "count": "[length(parameters('nodeTypes'))]", + "input": { + "applicationPorts": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'applicationPorts'), createObject('endPort', if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')].applicationPorts, 'endPort'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].applicationPorts.endPort, null()), 'startPort', if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')].applicationPorts, 'startPort'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].applicationPorts.startPort, null())), null())]", + "capacities": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'capacities'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].capacities, null())]", + "clientConnectionEndpointPort": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'clientConnectionEndpointPort'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].clientConnectionEndpointPort, null())]", + "durabilityLevel": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'durabilityLevel'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].durabilityLevel, null())]", + "ephemeralPorts": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'ephemeralPorts'), createObject('endPort', if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')].ephemeralPorts, 'endPort'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].ephemeralPorts.endPort, null()), 'startPort', if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')].ephemeralPorts, 'startPort'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].ephemeralPorts.startPort, null())), null())]", + "httpGatewayEndpointPort": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'httpGatewayEndpointPort'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].httpGatewayEndpointPort, null())]", + "isPrimary": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'isPrimary'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].isPrimary, null())]", + "isStateless": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'isStateless'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].isStateless, null())]", + "multipleAvailabilityZones": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'multipleAvailabilityZones'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].multipleAvailabilityZones, null())]", + "name": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'name'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].name, 'Node00')]", + "placementProperties": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'placementProperties'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].placementProperties, null())]", + "reverseProxyEndpointPort": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'reverseProxyEndpointPort'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].reverseProxyEndpointPort, null())]", + "vmInstanceCount": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'vmInstanceCount'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].vmInstanceCount, 1)]" + } + }, + { + "name": "notificationsVar", + "count": "[length(parameters('notifications'))]", + "input": { + "isEnabled": "[if(contains(parameters('notifications')[copyIndex('notificationsVar')], 'isEnabled'), parameters('notifications')[copyIndex('notificationsVar')].isEnabled, false())]", + "notificationCategory": "[if(contains(parameters('notifications')[copyIndex('notificationsVar')], 'notificationCategory'), parameters('notifications')[copyIndex('notificationsVar')].notificationCategory, 'WaveProgress')]", + "notificationLevel": "[if(contains(parameters('notifications')[copyIndex('notificationsVar')], 'notificationLevel'), parameters('notifications')[copyIndex('notificationsVar')].notificationLevel, 'All')]", + "notificationTargets": "[if(contains(parameters('notifications')[copyIndex('notificationsVar')], 'notificationTargets'), parameters('notifications')[copyIndex('notificationsVar')].notificationTargets, createArray())]" + } + } + ], + "upgradeDescriptionVar": "[union(createObject('deltaHealthPolicy', createObject('applicationDeltaHealthPolicies', coalesce(tryGet(parameters('upgradeDescription'), 'applicationDeltaHealthPolicies'), createObject()), 'maxPercentDeltaUnhealthyApplications', coalesce(tryGet(parameters('upgradeDescription'), 'maxPercentDeltaUnhealthyApplications'), 0), 'maxPercentDeltaUnhealthyNodes', coalesce(tryGet(parameters('upgradeDescription'), 'maxPercentDeltaUnhealthyNodes'), 0), 'maxPercentUpgradeDomainDeltaUnhealthyNodes', coalesce(tryGet(parameters('upgradeDescription'), 'maxPercentUpgradeDomainDeltaUnhealthyNodes'), 0)), 'forceRestart', if(contains(parameters('upgradeDescription'), 'forceRestart'), parameters('upgradeDescription').forceRestart, false()), 'healthCheckRetryTimeout', if(contains(parameters('upgradeDescription'), 'healthCheckRetryTimeout'), parameters('upgradeDescription').healthCheckRetryTimeout, '00:45:00'), 'healthCheckStableDuration', if(contains(parameters('upgradeDescription'), 'healthCheckStableDuration'), parameters('upgradeDescription').healthCheckStableDuration, '00:01:00'), 'healthCheckWaitDuration', if(contains(parameters('upgradeDescription'), 'healthCheckWaitDuration'), parameters('upgradeDescription').healthCheckWaitDuration, '00:00:30'), 'upgradeDomainTimeout', if(contains(parameters('upgradeDescription'), 'upgradeDomainTimeout'), parameters('upgradeDescription').upgradeDomainTimeout, '02:00:00'), 'upgradeReplicaSetCheckTimeout', if(contains(parameters('upgradeDescription'), 'upgradeReplicaSetCheckTimeout'), parameters('upgradeDescription').upgradeReplicaSetCheckTimeout, '1.00:00:00'), 'upgradeTimeout', if(contains(parameters('upgradeDescription'), 'upgradeTimeout'), parameters('upgradeDescription').upgradeTimeout, '02:00:00')), if(contains(parameters('upgradeDescription'), 'healthPolicy'), createObject('healthPolicy', createObject('applicationHealthPolicies', if(contains(parameters('upgradeDescription').healthPolicy, 'applicationHealthPolicies'), parameters('upgradeDescription').healthPolicy.applicationHealthPolicies, createObject()), 'maxPercentUnhealthyApplications', if(contains(parameters('upgradeDescription').healthPolicy, 'maxPercentUnhealthyApplications'), parameters('upgradeDescription').healthPolicy.maxPercentUnhealthyApplications, 0), 'maxPercentUnhealthyNodes', if(contains(parameters('upgradeDescription').healthPolicy, 'maxPercentUnhealthyNodes'), parameters('upgradeDescription').healthPolicy.maxPercentUnhealthyNodes, 0))), createObject()))]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.servicefabric-cluster.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "serviceFabricCluster": { + "type": "Microsoft.ServiceFabric/clusters", + "apiVersion": "2021-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "addOnFeatures": "[parameters('addOnFeatures')]", + "applicationTypeVersionsCleanupPolicy": { + "maxUnusedVersionsToKeep": "[parameters('maxUnusedVersionsToKeep')]" + }, + "azureActiveDirectory": "[if(not(empty(parameters('azureActiveDirectory'))), createObject('clientApplication', if(contains(parameters('azureActiveDirectory'), 'clientApplication'), parameters('azureActiveDirectory').clientApplication, null()), 'clusterApplication', if(contains(parameters('azureActiveDirectory'), 'clusterApplication'), parameters('azureActiveDirectory').clusterApplication, null()), 'tenantId', if(contains(parameters('azureActiveDirectory'), 'tenantId'), parameters('azureActiveDirectory').tenantId, null())), null())]", + "certificate": "[if(not(empty(parameters('certificate'))), createObject('thumbprint', if(contains(parameters('certificate'), 'thumbprint'), parameters('certificate').thumbprint, null()), 'thumbprintSecondary', if(contains(parameters('certificate'), 'thumbprintSecondary'), parameters('certificate').thumbprintSecondary, null()), 'x509StoreName', if(contains(parameters('certificate'), 'x509StoreName'), parameters('certificate').x509StoreName, null())), null())]", + "certificateCommonNames": "[if(not(empty(parameters('certificateCommonNames'))), createObject('commonNames', if(contains(parameters('certificateCommonNames'), 'commonNames'), parameters('certificateCommonNames').commonNames, null()), 'x509StoreName', if(contains(parameters('certificateCommonNames'), 'certificateCommonNamesx509StoreName'), parameters('certificateCommonNames').certificateCommonNamesx509StoreName, null())), null())]", + "clientCertificateCommonNames": "[if(not(empty(parameters('clientCertificateCommonNames'))), variables('clientCertificateCommonNamesVar'), null())]", + "clientCertificateThumbprints": "[if(not(empty(parameters('clientCertificateThumbprints'))), variables('clientCertificateThumbprintsVar'), null())]", + "clusterCodeVersion": "[parameters('clusterCodeVersion')]", + "diagnosticsStorageAccountConfig": "[if(not(empty(parameters('diagnosticsStorageAccountConfig'))), createObject('blobEndpoint', if(contains(parameters('diagnosticsStorageAccountConfig'), 'blobEndpoint'), parameters('diagnosticsStorageAccountConfig').blobEndpoint, null()), 'protectedAccountKeyName', if(contains(parameters('diagnosticsStorageAccountConfig'), 'protectedAccountKeyName'), parameters('diagnosticsStorageAccountConfig').protectedAccountKeyName, null()), 'protectedAccountKeyName2', if(contains(parameters('diagnosticsStorageAccountConfig'), 'protectedAccountKeyName2'), parameters('diagnosticsStorageAccountConfig').protectedAccountKeyName2, null()), 'queueEndpoint', if(contains(parameters('diagnosticsStorageAccountConfig'), 'queueEndpoint'), parameters('diagnosticsStorageAccountConfig').queueEndpoint, null()), 'storageAccountName', if(contains(parameters('diagnosticsStorageAccountConfig'), 'storageAccountName'), parameters('diagnosticsStorageAccountConfig').storageAccountName, null()), 'tableEndpoint', if(contains(parameters('diagnosticsStorageAccountConfig'), 'tableEndpoint'), parameters('diagnosticsStorageAccountConfig').tableEndpoint, null())), null())]", + "eventStoreServiceEnabled": "[parameters('eventStoreServiceEnabled')]", + "fabricSettings": "[if(not(empty(parameters('fabricSettings'))), variables('fabricSettingsVar'), null())]", + "infrastructureServiceManager": "[parameters('infrastructureServiceManager')]", + "managementEndpoint": "[parameters('managementEndpoint')]", + "nodeTypes": "[if(not(empty(parameters('nodeTypes'))), variables('fnodeTypesVar'), createArray())]", + "notifications": "[if(not(empty(parameters('notifications'))), variables('notificationsVar'), null())]", + "reliabilityLevel": "[if(not(empty(parameters('reliabilityLevel'))), parameters('reliabilityLevel'), 'None')]", + "reverseProxyCertificate": "[if(not(empty(parameters('reverseProxyCertificate'))), createObject('thumbprint', if(contains(parameters('reverseProxyCertificate'), 'thumbprint'), parameters('reverseProxyCertificate').thumbprint, null()), 'thumbprintSecondary', if(contains(parameters('reverseProxyCertificate'), 'thumbprintSecondary'), parameters('reverseProxyCertificate').thumbprintSecondary, null()), 'x509StoreName', if(contains(parameters('reverseProxyCertificate'), 'x509StoreName'), parameters('reverseProxyCertificate').x509StoreName, null())), null())]", + "reverseProxyCertificateCommonNames": "[if(not(empty(parameters('reverseProxyCertificateCommonNames'))), createObject('commonNames', if(contains(parameters('reverseProxyCertificateCommonNames'), 'commonNames'), parameters('reverseProxyCertificateCommonNames').commonNames, null()), 'x509StoreName', if(contains(parameters('reverseProxyCertificateCommonNames'), 'x509StoreName'), parameters('reverseProxyCertificateCommonNames').x509StoreName, null())), null())]", + "sfZonalUpgradeMode": "[if(not(empty(parameters('sfZonalUpgradeMode'))), parameters('sfZonalUpgradeMode'), null())]", + "upgradeDescription": "[if(not(empty(parameters('upgradeDescription'))), variables('upgradeDescriptionVar'), null())]", + "upgradeMode": "[if(not(empty(parameters('upgradeMode'))), parameters('upgradeMode'), null())]", + "upgradePauseEndTimestampUtc": "[parameters('upgradePauseEndTimestampUtc')]", + "upgradePauseStartTimestampUtc": "[parameters('upgradePauseStartTimestampUtc')]", + "upgradeWave": "[if(not(empty(parameters('upgradeWave'))), parameters('upgradeWave'), null())]", + "vmImage": "[parameters('vmImage')]", + "vmssZonalUpgradeMode": "[if(not(empty(parameters('vmssZonalUpgradeMode'))), parameters('vmssZonalUpgradeMode'), null())]", + "waveUpgradePaused": "[parameters('waveUpgradePaused')]" + } + }, + "serviceFabricCluster_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.ServiceFabric/clusters/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "serviceFabricCluster" + ] + }, + "serviceFabricCluster_roleAssignments": { + "copy": { + "name": "serviceFabricCluster_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ServiceFabric/clusters/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.ServiceFabric/clusters', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "serviceFabricCluster" + ] + }, + "serviceFabricCluster_applicationTypes": { + "copy": { + "name": "serviceFabricCluster_applicationTypes", + "count": "[length(parameters('applicationTypes'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-SFC-{1}', uniqueString(deployment().name, parameters('location')), parameters('applicationTypes')[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationTypes')[copyIndex()].name]" + }, + "serviceFabricClusterName": { + "value": "[parameters('name')]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('applicationTypes')[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "4241007545422374207" + }, + "name": "Service Fabric Cluster Application Types", + "description": "This module deploys a Service Fabric Cluster Application Type.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "serviceFabricClusterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Service Fabric cluster. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "defaultApplicationType", + "metadata": { + "description": "Optional. Application type name." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "serviceFabricCluster": { + "existing": true, + "type": "Microsoft.ServiceFabric/clusters", + "apiVersion": "2021-06-01", + "name": "[parameters('serviceFabricClusterName')]" + }, + "applicationTypes": { + "type": "Microsoft.ServiceFabric/clusters/applicationTypes", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}', parameters('serviceFabricClusterName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "dependsOn": [ + "serviceFabricCluster" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The resource name of the Application type." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the Application type." + }, + "value": "[resourceGroup().name]" + }, + "resourceID": { + "type": "string", + "metadata": { + "description": "The resource ID of the Application type." + }, + "value": "[resourceId('Microsoft.ServiceFabric/clusters/applicationTypes', parameters('serviceFabricClusterName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "serviceFabricCluster" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Service Fabric Cluster name." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The Service Fabric Cluster resource group." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The Service Fabric Cluster resource ID." + }, + "value": "[resourceId('Microsoft.ServiceFabric/clusters', parameters('name'))]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The Service Fabric Cluster endpoint." + }, + "value": "[reference('serviceFabricCluster').clusterEndpoint]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('serviceFabricCluster', '2021-06-01', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/service-fabric/cluster/tests/e2e/cert/main.test.bicep b/avm/res/service-fabric/cluster/tests/e2e/cert/main.test.bicep new file mode 100644 index 0000000000..3b291fce05 --- /dev/null +++ b/avm/res/service-fabric/cluster/tests/e2e/cert/main.test.bicep @@ -0,0 +1,71 @@ +targetScope = 'subscription' + +metadata name = 'Certificate' +metadata description = 'This instance deploys the module with a certificate.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-servicefabric.clusters-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'sfccer' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}${serviceShort}001' + managementEndpoint: 'https://${namePrefix}${serviceShort}001.westeurope.cloudapp.azure.com:19080' + reliabilityLevel: 'None' + certificate: { + thumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + x509StoreName: 'My' + } + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + name: 'Node01' + } + ] + } + } +] diff --git a/avm/res/service-fabric/cluster/tests/e2e/defaults/main.test.bicep b/avm/res/service-fabric/cluster/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..37e0d4b9c5 --- /dev/null +++ b/avm/res/service-fabric/cluster/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,67 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-servicefabric.clusters-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'sfcmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}${serviceShort}001' + managementEndpoint: 'https://${namePrefix}${serviceShort}001.westeurope.cloudapp.azure.com:19080' + reliabilityLevel: 'None' + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + name: 'Node01' + } + ] + } + } +] diff --git a/avm/res/service-fabric/cluster/tests/e2e/max/dependencies.bicep b/avm/res/service-fabric/cluster/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..edbdc3dded --- /dev/null +++ b/avm/res/service-fabric/cluster/tests/e2e/max/dependencies.bicep @@ -0,0 +1,31 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the storage account to create.') +param storageAccountName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = { + name: storageAccountName + location: location + kind: 'StorageV2' + sku: { + name: 'Standard_LRS' + } + properties: { + allowBlobPublicAccess: false + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The name of the created Storage Account.') +output storageAccountName string = storageAccount.name diff --git a/avm/res/service-fabric/cluster/tests/e2e/max/main.test.bicep b/avm/res/service-fabric/cluster/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..8681e32333 --- /dev/null +++ b/avm/res/service-fabric/cluster/tests/e2e/max/main.test.bicep @@ -0,0 +1,238 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set' +metadata description = 'This instance deploys the module with most of its features enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-servicefabric.clusters-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'sfcmax' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + storageAccountName: 'dep${namePrefix}azsa${serviceShort}01' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}${serviceShort}001' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + tags: { + 'hidden-title': 'This is visible in the resource name' + resourceType: 'Service Fabric' + clusterName: '${namePrefix}${serviceShort}001' + } + addOnFeatures: [ + 'RepairManager' + 'DnsService' + 'BackupRestoreService' + 'ResourceMonitorService' + ] + maxUnusedVersionsToKeep: 2 + azureActiveDirectory: { + clientApplication: nestedDependencies.outputs.managedIdentityPrincipalId + clusterApplication: 'cf33fea8-b30f-424f-ab73-c48d99e0b222' + tenantId: tenant().tenantId + } + certificateCommonNames: { + commonNames: [ + { + certificateCommonName: 'certcommon' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + } + ] + x509StoreName: '' + } + clientCertificateCommonNames: [ + { + certificateCommonName: 'clientcommoncert1' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateCommonName: 'clientcommoncert2' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + clientCertificateThumbprints: [ + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + diagnosticsStorageAccountConfig: { + blobEndpoint: 'https://${nestedDependencies.outputs.storageAccountName}.blob.${environment().suffixes.storage}/' + protectedAccountKeyName: 'StorageAccountKey1' + queueEndpoint: 'https://${nestedDependencies.outputs.storageAccountName}.queue.${environment().suffixes.storage}/' + storageAccountName: nestedDependencies.outputs.storageAccountName + tableEndpoint: 'https://${nestedDependencies.outputs.storageAccountName}.table.${environment().suffixes.storage}/' + } + fabricSettings: [ + { + name: 'Security' + parameters: [ + { + name: 'ClusterProtectionLevel' + value: 'EncryptAndSign' + } + ] + } + { + name: 'UpgradeService' + parameters: [ + { + name: 'AppPollIntervalInSeconds' + value: '60' + } + ] + } + ] + managementEndpoint: 'https://${namePrefix}${serviceShort}001.westeurope.cloudapp.azure.com:19080' + reliabilityLevel: 'Silver' + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Silver' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + name: 'Node01' + + isStateless: false + multipleAvailabilityZones: false + + placementProperties: {} + reverseProxyEndpointPort: '' + vmInstanceCount: 5 + } + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 64000 + startPort: 49000 + httpGatewayEndpointPort: 19007 + isPrimary: true + name: 'Node02' + vmInstanceCount: 5 + } + } + ] + notifications: [ + { + isEnabled: true + notificationCategory: 'WaveProgress' + notificationLevel: 'Critical' + notificationTargets: [ + { + notificationChannel: 'EmailUser' + receivers: [ + 'SomeReceiver' + ] + } + ] + } + ] + upgradeDescription: { + forceRestart: false + upgradeReplicaSetCheckTimeout: '1.00:00:00' + healthCheckWaitDuration: '00:00:30' + healthCheckStableDuration: '00:01:00' + healthCheckRetryTimeout: '00:45:00' + upgradeTimeout: '02:00:00' + upgradeDomainTimeout: '02:00:00' + healthPolicy: { + maxPercentUnhealthyNodes: 0 + maxPercentUnhealthyApplications: 0 + } + deltaHealthPolicy: { + maxPercentDeltaUnhealthyNodes: 0 + maxPercentUpgradeDomainDeltaUnhealthyNodes: 0 + maxPercentDeltaUnhealthyApplications: 0 + } + } + vmImage: 'Linux' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + applicationTypes: [ + { + name: 'WordCount' + } + ] + } + } +] diff --git a/avm/res/service-fabric/cluster/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/service-fabric/cluster/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..edbdc3dded --- /dev/null +++ b/avm/res/service-fabric/cluster/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,31 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the storage account to create.') +param storageAccountName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = { + name: storageAccountName + location: location + kind: 'StorageV2' + sku: { + name: 'Standard_LRS' + } + properties: { + allowBlobPublicAccess: false + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The name of the created Storage Account.') +output storageAccountName string = storageAccount.name diff --git a/avm/res/service-fabric/cluster/tests/e2e/waf-aligned/main.test.bicep b/avm/res/service-fabric/cluster/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..cee548743e --- /dev/null +++ b/avm/res/service-fabric/cluster/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,214 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-servicefabric.clusters-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'sfcwaf' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + storageAccountName: 'dep${namePrefix}azsa${serviceShort}01' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}${serviceShort}001' + tags: { + 'hidden-title': 'This is visible in the resource name' + resourceType: 'Service Fabric' + clusterName: '${namePrefix}${serviceShort}001' + } + addOnFeatures: [ + 'RepairManager' + 'DnsService' + 'BackupRestoreService' + 'ResourceMonitorService' + ] + maxUnusedVersionsToKeep: 2 + azureActiveDirectory: { + clientApplication: nestedDependencies.outputs.managedIdentityPrincipalId + clusterApplication: 'cf33fea8-b30f-424f-ab73-c48d99e0b222' + tenantId: tenant().tenantId + } + certificateCommonNames: { + commonNames: [ + { + certificateCommonName: 'certcommon' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + } + ] + x509StoreName: '' + } + clientCertificateCommonNames: [ + { + certificateCommonName: 'clientcommoncert1' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateCommonName: 'clientcommoncert2' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + clientCertificateThumbprints: [ + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + diagnosticsStorageAccountConfig: { + blobEndpoint: 'https://${nestedDependencies.outputs.storageAccountName}.blob.${environment().suffixes.storage}/' + protectedAccountKeyName: 'StorageAccountKey1' + queueEndpoint: 'https://${nestedDependencies.outputs.storageAccountName}.queue.${environment().suffixes.storage}/' + storageAccountName: nestedDependencies.outputs.storageAccountName + tableEndpoint: 'https://${nestedDependencies.outputs.storageAccountName}.table.${environment().suffixes.storage}/' + } + fabricSettings: [ + { + name: 'Security' + parameters: [ + { + name: 'ClusterProtectionLevel' + value: 'EncryptAndSign' + } + ] + } + { + name: 'UpgradeService' + parameters: [ + { + name: 'AppPollIntervalInSeconds' + value: '60' + } + ] + } + ] + managementEndpoint: 'https://${namePrefix}${serviceShort}001.westeurope.cloudapp.azure.com:19080' + reliabilityLevel: 'Silver' + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Silver' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + name: 'Node01' + + isStateless: false + multipleAvailabilityZones: false + + placementProperties: {} + reverseProxyEndpointPort: '' + vmInstanceCount: 5 + } + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 64000 + startPort: 49000 + httpGatewayEndpointPort: 19007 + isPrimary: true + name: 'Node02' + vmInstanceCount: 5 + } + } + ] + notifications: [ + { + isEnabled: true + notificationCategory: 'WaveProgress' + notificationLevel: 'Critical' + notificationTargets: [ + { + notificationChannel: 'EmailUser' + receivers: [ + 'SomeReceiver' + ] + } + ] + } + ] + upgradeDescription: { + forceRestart: false + upgradeReplicaSetCheckTimeout: '1.00:00:00' + healthCheckWaitDuration: '00:00:30' + healthCheckStableDuration: '00:01:00' + healthCheckRetryTimeout: '00:45:00' + upgradeTimeout: '02:00:00' + upgradeDomainTimeout: '02:00:00' + healthPolicy: { + maxPercentUnhealthyNodes: 0 + maxPercentUnhealthyApplications: 0 + } + deltaHealthPolicy: { + maxPercentDeltaUnhealthyNodes: 0 + maxPercentUpgradeDomainDeltaUnhealthyNodes: 0 + maxPercentDeltaUnhealthyApplications: 0 + } + } + vmImage: 'Linux' + applicationTypes: [ + { + name: 'WordCount' // not idempotent + } + ] + } + } +] diff --git a/avm/res/service-fabric/cluster/version.json b/avm/res/service-fabric/cluster/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/res/service-fabric/cluster/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From 841a1f13c3b3956effa17056bd86aa417ea39078 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Wed, 10 Apr 2024 22:06:21 +0200 Subject: [PATCH 2/2] fix: Ported additional fixes for Path-Publish action to Tag-Publish workflow (#1637) ## Description - Ported additional fixes for Path-Publish action to Tag-Publish workflow - Additional output - Correct function reference ## Type of Change - [x] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --- .github/workflows/avm.platform.publish-tag.yml | 2 +- avm/utilities/pipelines/platform/Publish-ModuleFromTagToPBR.ps1 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/avm.platform.publish-tag.yml b/.github/workflows/avm.platform.publish-tag.yml index 702fe493f2..194531cad2 100644 --- a/.github/workflows/avm.platform.publish-tag.yml +++ b/.github/workflows/avm.platform.publish-tag.yml @@ -63,7 +63,7 @@ jobs: Write-Verbose 'Invoke function with' -Verbose Write-Verbose ($functionInput | ConvertTo-Json | Out-String) -Verbose - if($publishOutputs = Publish-ModuleFromPathToPBR @functionInput -Verbose) { + if($publishOutputs = Publish-ModuleFromTagToPBR @functionInput -Verbose) { $publishOutputs.Keys | Foreach-Object { Write-Verbose ('Passing pipeline variable [{0}] with value [{1}]' -f $_, $publishOutputs.$_) -Verbose Write-Output ('{0}={1}' -f $_, $publishOutputs.$_) >> $env:GITHUB_OUTPUT diff --git a/avm/utilities/pipelines/platform/Publish-ModuleFromTagToPBR.ps1 b/avm/utilities/pipelines/platform/Publish-ModuleFromTagToPBR.ps1 index 8d15039f1a..9e92d60d76 100644 --- a/avm/utilities/pipelines/platform/Publish-ModuleFromTagToPBR.ps1 +++ b/avm/utilities/pipelines/platform/Publish-ModuleFromTagToPBR.ps1 @@ -94,5 +94,6 @@ function Publish-ModuleFromTagToPBR { return @{ version = $targetVersion publishedModuleName = $moduleRelativeFolderPath + gitTagName = $ModuleReleaseTagName } }