Skip to content

Instantly share code, notes, and snippets.

@afarber
Created June 12, 2020 20:00
Show Gist options
  • Save afarber/7b3eea6c5dc036efe03534dd3b1a11b1 to your computer and use it in GitHub Desktop.
Save afarber/7b3eea6c5dc036efe03534dd3b1a11b1 to your computer and use it in GitHub Desktop.
sfcluster.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"clusterName": {
"type": "string",
"defaultValue": "sfcluster",
"minLength": 5,
"metadata": {
"description": "Name of the SF cluster"
}
},
"certificateThumbprint": {
"type": "string",
"metadata": {
"description": "Certificate Thumbprint"
}
},
"sourceVaultResourceId": {
"type": "string",
"metadata": {
"description": "Resource Id of the key vault, is should be in the format of /subscriptions/<Sub ID>/resourceGroups/<Resource group name>/providers/Microsoft.KeyVault/vaults/<vault name>"
}
},
"certificateUrlValue": {
"type": "string",
"metadata": {
"description": "Refers to the location URL in your key vault where the certificate was uploaded, it is should be in the format of https://<name of the vault>.vault.azure.net:443/secrets/<exact location>"
}
}
},
"variables": {
"clusterName": "[concat(resourceGroup().name, '-', parameters('clusterName'))]",
"dnsName": "[concat(resourceGroup().name, '-my')]",
"virtualNetworkName": "[concat(variables('dnsName'), '-VNet')]",
"addressPrefix": "10.0.0.0/16",
"nicName": "NIC",
"lbIPName": "[concat(variables('dnsName'), '-PublicIP')]",
"overProvision": false,
"nt0applicationStartPort": "20000",
"nt0applicationEndPort": "30000",
"nt0ephemeralStartPort": "49152",
"nt0ephemeralEndPort": "65534",
"nt0fabricTcpGatewayPort": "19000",
"nt0fabricHttpGatewayPort": "19080",
"subnet0Name": "Subnet-0",
"subnet0Prefix": "10.0.0.0/24",
"subnet0Ref": "[resourceId('Microsoft.Network/virtualNetworks/subnets/', variables('virtualNetworkName'), variables('subnet0Name'))]",
"supportLogStorageAccountName": "[concat( uniqueString(resourceGroup().id), 'logs')]",
"applicationDiagnosticsStorageAccountName": "[concat(uniqueString(resourceGroup().id), 'diag')]",
"lbName": "[concat(variables('dnsName'), '-LB')]",
"lbIPConfig0": "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations/', variables('lbName'), 'LoadBalancerIPConfig')]",
"lbPoolID0": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', variables('lbName'), 'LoadBalancerBEAddressPool')]",
"lbProbeID0": "[resourceId('Microsoft.Network/loadBalancers/probes', variables('lbName'), 'FabricGatewayProbe')]",
"lbHttpProbeID0": "[resourceId('Microsoft.Network/loadBalancers/probes', variables('lbName'), 'FabricHttpGatewayProbe')]",
"lbNatPoolID0": "[resourceId('Microsoft.Network/loadBalancers/inboundNatPools', variables('lbName'), 'LoadBalancerBEAddressNatPool')]",
"vmNodeType0Name": "nt1vm",
"vmNodeType0Size": "Standard_D2_v2"
},
"outputs": {
"Cluster-Host-Name": {
"type": "string",
"value": "[concat(variables('dnsName'), '-my.example.com')]"
},
"SSL-Certificate-Find-Value": {
"type": "string",
"value": "[parameters('certificateThumbprint')]"
}
},
"resources": [
{
"apiVersion": "2019-04-01",
"type": "Microsoft.Storage/storageAccounts",
"name": "[variables('supportLogStorageAccountName')]",
"location": "[resourceGroup().location]",
"properties": {},
"kind": "Storage",
"sku": {
"name": "Standard_LRS"
},
"tags": {
"resourceType": "Service Fabric",
"clusterName": "[variables('clusterName')]"
}
},
{
"apiVersion": "2019-04-01",
"type": "Microsoft.Storage/storageAccounts",
"name": "[variables('applicationDiagnosticsStorageAccountName')]",
"location": "[resourceGroup().location]",
"properties": {},
"kind": "Storage",
"sku": {
"name": "Standard_LRS"
},
"tags": {
"resourceType": "Service Fabric",
"clusterName": "[variables('clusterName')]"
}
},
{
"apiVersion": "2018-08-01",
"type": "Microsoft.Network/virtualNetworks",
"name": "[variables('virtualNetworkName')]",
"location": "[resourceGroup().location]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"subnets": [
{
"name": "[variables('subnet0Name')]",
"properties": {
"addressPrefix": "[variables('subnet0Prefix')]"
}
}
]
},
"tags": {
"resourceType": "Service Fabric",
"clusterName": "[variables('clusterName')]"
}
},
{
"apiVersion": "2018-08-01",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[variables('lbIPName')]",
"location": "[resourceGroup().location]",
"properties": {
"dnsSettings": {
"domainNameLabel": "[variables('dnsName')]"
},
"publicIPAllocationMethod": "Dynamic"
},
"tags": {
"resourceType": "Service Fabric",
"clusterName": "[variables('clusterName')]"
}
},
{
"apiVersion": "2018-08-01",
"type": "Microsoft.Network/loadBalancers",
"name": "[variables('lbName')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[variables('lbIPName')]"
],
"properties": {
"frontendIPConfigurations": [
{
"name": "LoadBalancerIPConfig",
"properties": {
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('lbIPName'))]"
}
}
}
],
"backendAddressPools": [
{
"name": "LoadBalancerBEAddressPool",
"properties": {}
}
],
"loadBalancingRules": [
{
"name": "LBRule",
"properties": {
"backendAddressPool": {
"id": "[variables('lbPoolID0')]"
},
"backendPort": "[variables('nt0fabricTcpGatewayPort')]",
"enableFloatingIP": false,
"frontendIPConfiguration": {
"id": "[variables('lbIPConfig0')]"
},
"frontendPort": "[variables('nt0fabricTcpGatewayPort')]",
"idleTimeoutInMinutes": 5,
"probe": {
"id": "[variables('lbProbeID0')]"
},
"protocol": "Tcp"
}
},
{
"name": "LBHttpRule",
"properties": {
"backendAddressPool": {
"id": "[variables('lbPoolID0')]"
},
"backendPort": "[variables('nt0fabricHttpGatewayPort')]",
"enableFloatingIP": false,
"frontendIPConfiguration": {
"id": "[variables('lbIPConfig0')]"
},
"frontendPort": "[variables('nt0fabricHttpGatewayPort')]",
"idleTimeoutInMinutes": 5,
"probe": {
"id": "[variables('lbHttpProbeID0')]"
},
"protocol": "Tcp"
}
},
{
"name": "AppPortLBRule1",
"properties": {
"backendAddressPool": {
"id": "[variables('lbPoolID0')]"
},
"backendPort": 80,
"enableFloatingIP": false,
"frontendIPConfiguration": {
"id": "[variables('lbIPConfig0')]"
},
"frontendPort": 80,
"idleTimeoutInMinutes": 5,
"probe": {
"id": "[resourceId('Microsoft.Network/loadBalancers/probes', variables('lbName'), 'AppPortProbe1')]"
},
"protocol": "Tcp"
}
},
{
"name": "AppPortLBRule2",
"properties": {
"backendAddressPool": {
"id": "[variables('lbPoolID0')]"
},
"backendPort": 9950,
"enableFloatingIP": false,
"frontendIPConfiguration": {
"id": "[variables('lbIPConfig0')]"
},
"frontendPort": 9950,
"idleTimeoutInMinutes": 5,
"probe": {
"id": "[resourceId('Microsoft.Network/loadBalancers/probes', variables('lbName'), 'AppPortProbe2')]"
},
"protocol": "Tcp"
}
}
],
"probes": [
{
"name": "FabricGatewayProbe",
"properties": {
"intervalInSeconds": 5,
"numberOfProbes": 2,
"port": "[variables('nt0fabricTcpGatewayPort')]",
"protocol": "Tcp"
}
},
{
"name": "FabricHttpGatewayProbe",
"properties": {
"intervalInSeconds": 5,
"numberOfProbes": 2,
"port": "[variables('nt0fabricHttpGatewayPort')]",
"protocol": "Tcp"
}
},
{
"name": "AppPortProbe1",
"properties": {
"intervalInSeconds": 5,
"numberOfProbes": 2,
"port": 80,
"protocol": "Tcp"
}
},
{
"name": "AppPortProbe2",
"properties": {
"intervalInSeconds": 5,
"numberOfProbes": 2,
"port": 9950,
"protocol": "Tcp"
}
}
],
"inboundNatPools": [
{
"name": "LoadBalancerBEAddressNatPool",
"properties": {
"backendPort": 3389,
"frontendIPConfiguration": {
"id": "[variables('lbIPConfig0')]"
},
"frontendPortRangeEnd": 4500,
"frontendPortRangeStart": 3389,
"protocol": "Tcp"
}
}
]
},
"tags": {
"resourceType": "Service Fabric",
"clusterName": "[variables('clusterName')]"
}
},
{
"apiVersion": "2018-10-01",
"type": "Microsoft.Compute/virtualMachineScaleSets",
"name": "[variables('vmNodeType0Name')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[variables('virtualNetworkName')]",
"[variables('lbName')]",
"[variables('supportLogStorageAccountName')]",
"[variables('applicationDiagnosticsStorageAccountName')]"
],
"properties": {
"overprovision": "[variables('overProvision')]",
"upgradePolicy": {
"mode": "Automatic"
},
"virtualMachineProfile": {
"extensionProfile": {
"extensions": [
{
"name": "[concat('ServiceFabricNodeVmExt','_vmNodeType0Name')]",
"properties": {
"type": "ServiceFabricNode",
"autoUpgradeMinorVersion": true,
"protectedSettings": {
"StorageAccountKey1": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('supportLogStorageAccountName')),'2015-05-01-preview').key1]",
"StorageAccountKey2": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('supportLogStorageAccountName')),'2015-05-01-preview').key2]"
},
"publisher": "Microsoft.Azure.ServiceFabric",
"settings": {
"clusterEndpoint": "[reference(variables('clusterName')).clusterEndpoint]",
"nodeTypeRef": "[variables('vmNodeType0Name')]",
"dataPath": "D:\\\\SvcFab",
"durabilityLevel": "Bronze",
"nicPrefixOverride": "[variables('subnet0Prefix')]",
"certificate": {
"thumbprint": "[parameters('certificateThumbprint')]",
"x509StoreName": "My"
}
},
"typeHandlerVersion": "1.0"
}
},
{
"name": "[concat('VMDiagnosticsVmExt','_vmNodeType0Name')]",
"properties": {
"type": "IaaSDiagnostics",
"autoUpgradeMinorVersion": true,
"protectedSettings": {
"storageAccountName": "[variables('applicationDiagnosticsStorageAccountName')]",
"storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('applicationDiagnosticsStorageAccountName')),'2015-05-01-preview').key1]",
"storageAccountEndPoint": "https://core.windows.net/"
},
"publisher": "Microsoft.Azure.Diagnostics",
"settings": {
"WadCfg": {
"DiagnosticMonitorConfiguration": {
"overallQuotaInMB": "50000",
"EtwProviders": {
"EtwEventSourceProviderConfiguration": [
{
"provider": "Microsoft-ServiceFabric-Actors",
"scheduledTransferKeywordFilter": "1",
"scheduledTransferPeriod": "PT5M",
"DefaultEvents": {
"eventDestination": "ServiceFabricReliableActorEventTable"
}
},
{
"provider": "Microsoft-ServiceFabric-Services",
"scheduledTransferPeriod": "PT5M",
"DefaultEvents": {
"eventDestination": "ServiceFabricReliableServiceEventTable"
}
}
],
"EtwManifestProviderConfiguration": [
{
"provider": "cbd93bc2-71e5-4566-b3a7-595d8eeca6e8",
"scheduledTransferLogLevelFilter": "Information",
"scheduledTransferKeywordFilter": "4611686018427387904",
"scheduledTransferPeriod": "PT5M",
"DefaultEvents": {
"eventDestination": "ServiceFabricSystemEventTable"
}
}
]
}
}
},
"StorageAccount": "[variables('applicationDiagnosticsStorageAccountName')]"
},
"typeHandlerVersion": "1.5"
}
}
]
},
"networkProfile": {
"networkInterfaceConfigurations": [
{
"name": "[concat(variables('nicName'), '-0')]",
"properties": {
"ipConfigurations": [
{
"name": "[concat(variables('nicName'),'-',0)]",
"properties": {
"loadBalancerBackendAddressPools": [
{
"id": "[variables('lbPoolID0')]"
}
],
"loadBalancerInboundNatPools": [
{
"id": "[variables('lbNatPoolID0')]"
}
],
"subnet": {
"id": "[variables('subnet0Ref')]"
}
}
}
],
"primary": true
}
}
]
},
"osProfile": {
"adminUsername": "RdpUsername",
"adminPassword": {
"reference": {
"keyVault": {
"id": "[parameters('sourceVaultResourceId')]"
},
"secretName": "RdpPassword"
}
},
"computernamePrefix": "[variables('vmNodeType0Name')]",
"secrets": [
{
"sourceVault": {
"id": "[parameters('sourceVaultResourceId')]"
},
"vaultCertificates": [
{
"certificateStore": "My",
"certificateUrl": "[parameters('certificateUrlValue')]"
}
]
}
]
},
"storageProfile": {
"imageReference": {
"publisher": "MicrosoftWindowsServer",
"offer": "WindowsServerSemiAnnual",
"sku": "Datacenter-Core-1803-with-Containers-smalldisk",
"version": "latest"
},
"osDisk": {
"managedDisk": {
"storageAccountType": "Standard_LRS"
},
"caching": "ReadOnly",
"createOption": "FromImage",
"diskSizeGB": 30
}
}
}
},
"sku": {
"name": "[variables('vmNodeType0Size')]",
"capacity": 3,
"tier": "Standard"
},
"tags": {
"resourceType": "Service Fabric",
"clusterName": "[variables('clusterName')]"
}
},
{
"apiVersion": "2018-02-01",
"type": "Microsoft.ServiceFabric/clusters",
"name": "[variables('clusterName')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[variables('supportLogStorageAccountName')]"
],
"properties": {
"certificate": {
"thumbprint": "[parameters('certificateThumbprint')]",
"x509StoreName": "My"
},
"clusterState": "Default",
"diagnosticsStorageAccountConfig": {
"blobEndpoint": "[reference(resourceId('Microsoft.Storage/storageAccounts', variables('supportLogStorageAccountName')), '2018-07-01').primaryEndpoints.blob]",
"protectedAccountKeyName": "StorageAccountKey1",
"queueEndpoint": "[reference(resourceId('Microsoft.Storage/storageAccounts', variables('supportLogStorageAccountName')), '2018-07-01').primaryEndpoints.queue]",
"storageAccountName": "[variables('supportLogStorageAccountName')]",
"tableEndpoint": "[reference(resourceId('Microsoft.Storage/storageAccounts', variables('supportLogStorageAccountName')), '2018-07-01').primaryEndpoints.table]"
},
"fabricSettings": [
{
"name": "Security",
"parameters": [
{
"name": "ClusterProtectionLevel",
"value": "EncryptAndSign"
}
]
}
],
"managementEndpoint": "[concat('https://',reference(variables('lbIPName')).dnsSettings.fqdn,':',variables('nt0fabricHttpGatewayPort'))]",
"nodeTypes": [
{
"name": "[variables('vmNodeType0Name')]",
"applicationPorts": {
"endPort": "[variables('nt0applicationEndPort')]",
"startPort": "[variables('nt0applicationStartPort')]"
},
"clientConnectionEndpointPort": "[variables('nt0fabricTcpGatewayPort')]",
"durabilityLevel": "Bronze",
"ephemeralPorts": {
"endPort": "[variables('nt0ephemeralEndPort')]",
"startPort": "[variables('nt0ephemeralStartPort')]"
},
"httpGatewayEndpointPort": "[variables('nt0fabricHttpGatewayPort')]",
"isPrimary": true,
"vmInstanceCount": 3
}
],
"provisioningState": "Default",
"reliabilityLevel": "Bronze",
"upgradeMode": "Automatic",
"vmImage": "Windows"
},
"tags": {
"resourceType": "Service Fabric",
"clusterName": "[variables('clusterName')]"
}
}
]
}
@afarber
Copy link
Author

afarber commented Jun 12, 2020

Here sflcuster-params.json:

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "clusterName": {
            "value": "sfcluster"
        }
    }
}

@afarber
Copy link
Author

afarber commented Jun 12, 2020

And here the Azure pipeline tasks:

      # deploy KeyVault by ARM template and output VinHashKey and SF Cluster certificate thumbsprint
      - task: AzureResourceManagerTemplateDeployment@3
        displayName: 'Deploy Keyvault'
        inputs:
          deploymentScope: 'Resource Group'
          subscriptionId: '${{ parameters.SubscriptionId }}'
          azureResourceManagerConnection: '${{ parameters.ArmConnection }}'
          action: 'Create Or Update Resource Group'
          resourceGroupName: '${{ parameters.ResourceGroupName }}'
          location: 'West Europe'
          templateLocation: 'Linked artifact'
          csmFile: '$(Build.SourcesDirectory)/pipelines/templates/keyvault.json'
          csmParametersFile: '$(Build.SourcesDirectory)/pipelines/templates/keyvault-params.json'
          deploymentMode: 'Incremental'

      # collect outputs from the above ARM task and put them into pipeline vars
      - task: ARM Outputs@5
        displayName: 'Collect Keyvault output'
        inputs:
          ConnectedServiceNameSelector: 'ConnectedServiceNameARM'
          ConnectedServiceNameARM: '${{ parameters.ArmConnection }}'
          resourceGroupName: '${{ parameters.ResourceGroupName }}'
          whenLastDeploymentIsFailed: 'fail'

      # import the certificate ccg-self-signed-cert into the Keyvault if it is missing there
      - task: AzurePowerShell@5
        displayName: 'Import certificate'
        inputs:
          azureSubscription: '${{ parameters.ArmConnection }}'
          ScriptType: 'InlineScript'
          azurePowerShellVersion: '3.1.0'
          Inline: |
            $Cert = Get-AzKeyVaultCertificate -VaultName $(KeyVaultName) -Name my-self-signed-cert
            if (!$Cert) {
                $Pwd = ConvertTo-SecureString -String MyPassword -Force -AsPlainText
                $Base64 = 'MII....gfQ'
                $Cert = Import-AzKeyVaultCertificate -VaultName $(KeyVaultName) -Name my-self-signed-cert -CertificateString $Base64 -Password $Pwd
            }
            # set the pipeline variables Thumbprint and SecretId - needed for SF deployment
            echo "##vso[task.setvariable variable=Thumbprint]$($Cert.Thumbprint)"
            echo "##vso[task.setvariable variable=SecretId]$($Cert.SecretId)"

      # deploy SF cluster by ARM template and use the SF certificate thumbsprint as admin cert
      - task: AzureResourceManagerTemplateDeployment@3
        displayName: 'Deploy SF cluster'
        inputs:
          deploymentScope: 'Resource Group'
          subscriptionId: '${{ parameters.SubscriptionId }}'
          azureResourceManagerConnection: '${{ parameters.ArmConnection }}'
          action: 'Create Or Update Resource Group'
          resourceGroupName: '${{ parameters.ResourceGroupName }}'
          location: 'West Europe'
          templateLocation: 'Linked artifact'
          csmFile: '$(Build.SourcesDirectory)/pipelines/templates/sfcluster.json'
          csmParametersFile: '$(Build.SourcesDirectory)/pipelines/templates/sfcluster-params.json'
          overrideParameters: '-certificateThumbprint $(Thumbprint) -sourceVaultResourceId $(KeyvaultId) -certificateUrlValue $(SecretId)'
          deploymentMode: 'Incremental'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment