You've successfully subscribed to Florin Loghiade
Great! Next, complete checkout for full access to Florin Loghiade
Welcome back! You've successfully signed in
Success! Your account is fully activated, you now have access to all content.
Azure Service Fabric in production -Field notes

Azure Service Fabric in production -Field notes

in

I’ve been holding off this post for a while now to gather more information, to figure out how things can be done better and so on. This post is a culmination of experiences I’ve had with Service Fabric, how I solved them and hopefully solve your issues before you have a disaster on your hands.

Starting from the beginning. What is Service Fabric?

Service Fabric is a distributed systems platform that makes it easy to package, deploy, and manage scalable and reliable microservices and containers. Service Fabric also addresses the significant challenges in developing and managing cloud native applications. Developers and administrators can avoid complex infrastructure problems and focus on implementing mission-critical, demanding workloads that are scalable, reliable, and manageable. Service Fabric represents the next-generation platform for building and managing these enterprise-class, tier-1, cloud-scale applications running in containers.

Source: Azure docs

That being said, I have a small list of recommendations that should be enforced in practice so that you don’t repeat the mistakes I had to repair.

Recommendation 1: Provision your Service Fabric Clusters from a pipeline like VSTS, Jenkins, TeamCity and use ARM as much as possible. I’ve learned from the master Jeffrey Snover when he talked about PowerShell DSC that you should treat you VMs as cattle not pets. I took that advice to heart and it saved me multiple times. If you put in the time to develop the scripts/arm templates to deploy your clusters then when something bad happens you start from scratch.

I’ve done it two times in the last four years. Everything that is Service Fabric related is automated in such a way that if a major cluster failure happens then in 30 minutes it’s back up and running.

Recommendation 2: When in doubt, have multiple application packages. This one is more of a design decision and evolved from multiple failures in production. It started out as a single app package with N services under it and the problem was that if one service died then all of them died. That’s not the idea of microservices so we decided to decouple them to not take down everything when a crash happens.

Recommendation 3: Don’t use self-signed certificates in production clusters (Yes, I’ve seen this one happen multiple times). I mean it, never use them and as a second-best practice, try to use a client and server certificate for our endpoints. If for whatever reason the certificate expires before you update the cluster then you’re going to have a total meltdown which will force you to redeploy the cluster.

When I say that you’re going to have a total meltdown, it means that the cluster goes in an insecure state and the system stops trusting the other nodes and no matter what you do it won’t restart. If debugging the problem takes more time than redeploying from scratch, delete and deploy.

Recommendation 4: If you really want HTTPS for your microservices then you need to be very specific about it because HTTP is hardcoded in the code. First of all, you need to instantiate an endpoint protocol for each of your microservices in the ServiceManifest.xml and bind the certificate in the code.

# Between <Endpoints></Endpoints>
 <Endpoint Protocol="https" Name="ServiceEndpointHttps" Type="Input" Port="443" />

# Between <ServiceManifestImport></ServiceManifestImport>
<ServiceManifestRef ServiceManifestName="SampleWebApiPkg" ServiceManifestVersion="1.0.0" />
       <ConfigOverrides />
      <Policies>
         <EndpointBindingPolicy EndpointRef="ServiceEndpointHttps" CertificateRef="MyCert" />
      </Policies>
   <Certificates>
      <EndpointCertificate X509FindValue="1234567890ABCDEFEDCBA0987654321AABBCCDDE" Name="MyCert" />
   </Certificates></pre>

If you check OwinCommunicationListener::OpenAsync() you will find that this.listeningAddress has HTTP hardcoded, so obviously it doesn’t’ work with HTTPS.

# Change CreateServiceInstanceListeners from:

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new[]
    {
        new ServiceInstanceListener(serviceContext => new OwinCommunicationListener(Startup.ConfigureApp, serviceContext, ServiceEventSource.Current, "ServiceEndpoint"))
    };
}

# To
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
 {
      var endpoints = Context.CodePackageActivationContext.GetEndpoints()
                                   .Where(endpoint => endpoint.Protocol == EndpointProtocol.Http || endpoint.Protocol == EndpointProtocol.Https)
                                   .Select(endpoint => endpoint.Name);

            return endpoints.Select(endpoint => new ServiceInstanceListener(
                serviceContext => new OwinCommunicationListener(Startup.ConfigureApp, serviceContext, ServiceEventSource.Current, endpoint), endpoint));
         }
     }
 }


# This piece of code create a listener

Recommendation 5: Plan for outages and deploy accordingly. With Service Fabric you’re limited to an Availability Set and you don’t have access to Availability Zones to gain that 99.99% Availability SLA. That being said even with AZs, you should have DR ready for a serious outage like the cases where you need to wait 30 minutes for the cluster to be recreated. Use Traffic Manager in Priority Mode for an SFC specific endpoint (not an application) and test it every six months.

Recommendation 6: If you’re a decision-maker, don’t accept VPNs to connect to third-party services or any other things like that. This complicates the design and everything attached to it. I’ve been in this situation and it’s not pretty.

That being said, Service Fabric supports the deployment in dual load balancer mode with external and internal LBs. I’ve deployed a configuration like this and it quite works. ARM Template below:

ARM Template to deploy double LB clusters – External and Internal.

{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "clusterLocation": {
            "type": "string",
            "metadata": {
                "description": "Location of the Cluster"
            }
        },
        "clusterName": {
            "defaultValue": "Cluster",
            "type": "string",
            "metadata": {
                "description": "Name of your cluster - Between 3 and 23 characters. Letters and numbers only"
            }
        },
        "nt0applicationStartPort": {
            "defaultValue": 20000,
            "type": "int"
        },
        "nt0applicationEndPort": {
            "defaultValue": 30000,
            "type": "int"
        },
        "nt0ephemeralStartPort": {
            "defaultValue": 49152,
            "type": "int"
        },
        "nt0ephemeralEndPort": {
            "defaultValue": 65534,
            "type": "int"
        },
        "nt0fabricTcpGatewayPort": {
            "defaultValue": 19000,
            "type": "int"
        },
        "nt0fabricHttpGatewayPort": {
            "defaultValue": 19080,
            "type": "int"
        },
        "subnet0Name": {
            "defaultValue": "Subnet-0",
            "type": "string"
        },
        "subnet0Prefix": {
            "defaultValue": "172.16.242.0/28",
            "type": "string"
        },
        "computeLocation": {
            "type": "string"
        },
        "existingStaticIPResourceGroup": {
            "type": "string"
        },
        "existingStaticIPName": {
            "type": "string"
        },
        "existingStaticIPDnsFQDN": {
            "type": "string"
        },
        "adminUserName": {
            "defaultValue": "prodadm",
            "type": "string",
            "metadata": {
                "description": "Remote desktop user Id"
            }
        },
        "adminPassword": {
            "type": "securestring",
            "metadata": {
                "description": "Remote desktop user password. Must be a strong password"
            }
        },
        "virtualNetworkName": {
            "defaultValue": "VNet",
            "type": "string"
        },
        "addressPrefix": {
            "defaultValue": "172.16.242.0/27",
            "type": "string"
        },
        "nicName": {
            "defaultValue": "NIC",
            "type": "string"
        },
        "internalLBAddress": {
            "defaultValue": "172.16.242.14",
            "type": "string"
        },
        "overProvision": {
            "defaultValue": "false",
            "type": "string"
        },
        "vmImagePublisher": {
            "defaultValue": "MicrosoftWindowsServer",
            "type": "string"
        },
        "vmImageOffer": {
            "defaultValue": "WindowsServer",
            "type": "string"
        },
        "vmImageSku": {
            "defaultValue": "2016-Datacenter",
            "type": "string"
        },
        "vmImageVersion": {
            "defaultValue": "latest",
            "type": "string"
        },
        "loadBalancedAppPort1": {
            "defaultValue": 8080,
            "type": "int",
            "metadata": {
                "description": "App1"
            }
        },
        "loadBalancedAppPort2": {
            "defaultValue": 8081,
            "type": "int",
            "metadata": {
                "description": "App2"
            }
        },
        "clusterProtectionLevel": {
            "defaultValue": "EncryptAndSign",
            "allowedValues": [
                "None",
                "Sign",
                "EncryptAndSign"
            ],
            "type": "string",
            "metadata": {
                "description": "Protection level.Three values are allowed - EncryptAndSign, Sign, None. It is best to keep the default of EncryptAndSign, unless you have a need not to"
            }
        },
        "certificateStoreValue": {
            "defaultValue": "My",
            "allowedValues": [
                "My"
            ],
            "type": "string",
            "metadata": {
                "description": "The store name where the cert will be deployed in the virtual machine"
            }
        },
        "certificateThumbprint": {
            "type": "string",
            "metadata": {
                "description": "Certificate Thumbprint"
            }
        },
        "sourceVaultValue": {
            "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>"
            }
        },
        "supportLogStorageAccountType": {
            "defaultValue": "Standard_LRS",
            "allowedValues": [
                "Standard_LRS",
                "Standard_GRS"
            ],
            "type": "string",
            "metadata": {
                "description": "Replication option for the support log storage account"
            }
        },
        "supportLogStorageAccountName": {
            "defaultValue": "[toLower( concat('sflogs', uniquestring(resourceGroup().id),'2'))]",
            "type": "string",
            "metadata": {
                "description": "Name for the storage account that contains support logs from the cluster"
            }
        },
        "applicationDiagnosticsStorageAccountType": {
            "defaultValue": "Standard_LRS",
            "allowedValues": [
                "Standard_LRS",
                "Standard_GRS"
            ],
            "type": "string",
            "metadata": {
                "description": "Replication option for the application diagnostics storage account"
            }
        },
        "applicationDiagnosticsStorageAccountName": {
            "defaultValue": "[toLower(concat(uniquestring(resourceGroup().id), '3' ))]",
            "type": "string",
            "metadata": {
                "description": "Name for the storage account that contains application diagnostics data from the cluster"
            }
        },
        "nt0InstanceCount": {
            "defaultValue": 5,
            "type": "int",
            "metadata": {
                "description": "Instance count for node type"
            }
        },
        "vmNodeType0Name": {
            "defaultValue": "node1",
            "maxLength": 9,
            "type": "string"
        },
        "vmNodeType0Size": {
            "defaultValue": "Standard_D2_v2",
            "type": "string"
        },
        "applicationInsightsKey": {
            "type": "string"
        }
    },
    "variables": {
        "vmssApiVersion": "2017-03-30",
        "lbApiVersion": "2015-06-15",
        "vNetApiVersion": "2015-06-15",
        "storageApiVersion": "2016-01-01",
        "vnetID": "[resourceId('Microsoft.Network/virtualNetworks',parameters('virtualNetworkName'))]",
        "subnet0Ref": "[concat(variables('vnetID'),'/subnets/',parameters('subnet0Name'))]",
        "lbID0": "[resourceId('Microsoft.Network/loadBalancers', concat('LB','-', parameters('clusterName'),'-',parameters('vmNodeType0Name')))]",
        "lbIPConfig0": "[concat(variables('lbID0'),'/frontendIPConfigurations/LoadBalancerIPConfig')]",
        "lbPoolID0": "[concat(variables('lbID0'),'/backendAddressPools/LoadBalancerBEAddressPool')]",
        "lbProbeID0": "[concat(variables('lbID0'),'/probes/FabricGatewayProbe')]",
        "lbHttpProbeID0": "[concat(variables('lbID0'),'/probes/FabricHttpGatewayProbe')]",
        "existingStaticIP": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('existingStaticIPResourceGroup'), '/providers/Microsoft.Network/publicIPAddresses/', parameters('existingStaticIPName'))]",
        "lbID0-int": "[resourceId('Microsoft.Network/loadBalancers', concat('LB','-', parameters('clusterName'),'-',parameters('vmNodeType0Name'), '-internal'))]",
        "lbIPConfig0-int": "[concat(variables('lbID0-int'),'/frontendIPConfigurations/LoadBalancerIPConfig')]",
        "lbPoolID0-int": "[concat(variables('lbID0-int'),'/backendAddressPools/LoadBalancerBEAddressPool')]",
        "lbNatPoolID0-int": "[concat(variables('lbID0-int'),'/inboundNatPools/LoadBalancerBEAddressNatPool')]"
    },
    "resources": [
        {
            "type": "Microsoft.Storage/storageAccounts",
            "sku": {
                "name": "[parameters('supportLogStorageAccountType')]"
            },
            "kind": "Storage",
            "name": "[parameters('supportLogStorageAccountName')]",
            "apiVersion": "[variables('storageApiVersion')]",
            "location": "[parameters('computeLocation')]",
            "tags": {
                "resourceType": "Service Fabric",
                "clusterName": "[parameters('clusterName')]"
            },
            "properties": {},
            "dependsOn": []
        },
        {
            "type": "Microsoft.Storage/storageAccounts",
            "sku": {
                "name": "[parameters('applicationDiagnosticsStorageAccountType')]"
            },
            "kind": "Storage",
            "name": "[parameters('applicationDiagnosticsStorageAccountName')]",
            "apiVersion": "[variables('storageApiVersion')]",
            "location": "[parameters('computeLocation')]",
            "tags": {
                "resourceType": "Service Fabric",
                "clusterName": "[parameters('clusterName')]"
            },
            "properties": {},
            "dependsOn": []
        },
        {
            "type": "Microsoft.Network/virtualNetworks",
            "name": "[parameters('virtualNetworkName')]",
            "apiVersion": "[variables('vNetApiVersion')]",
            "location": "[parameters('computeLocation')]",
            "tags": {
                "resourceType": "Service Fabric",
                "clusterName": "[parameters('clusterName')]"
            },
            "properties": {
                "addressSpace": {
                    "addressPrefixes": [
                        "[parameters('addressPrefix')]"
                    ]
                },
                "subnets": [
                    {
                        "name": "[parameters('subnet0Name')]",
                        "properties": {
                            "addressPrefix": "[parameters('subnet0Prefix')]"
                        }
                    },
                    {
                        "name": "GatewaySubnet",
                        "properties": {
                            "addressPrefix": "172.16.242.16/28"
                        }
                    }
                ]
            },
            "dependsOn": []
        },
        {
            "type": "Microsoft.Network/loadBalancers",
            "name": "[concat('LB','-', parameters('clusterName'),'-',parameters('vmNodeType0Name'))]",
            "apiVersion": "[variables('lbApiVersion')]",
            "location": "[parameters('computeLocation')]",
            "tags": {
                "resourceType": "Service Fabric",
                "clusterName": "[parameters('clusterName')]"
            },
            "properties": {
                "frontendIPConfigurations": [
                    {
                        "name": "LoadBalancerIPConfig",
                        "properties": {
                            "publicIPAddress": {
                                "id": "[variables('existingStaticIP')]"
                            }
                        }
                    }
                ],
                "backendAddressPools": [
                    {
                        "name": "LoadBalancerBEAddressPool",
                        "properties": {}
                    }
                ],
                "loadBalancingRules": [
                    {
                        "name": "LBRule",
                        "properties": {
                            "backendAddressPool": {
                                "id": "[variables('lbPoolID0')]"
                            },
                            "backendPort": "[parameters('nt0fabricTcpGatewayPort')]",
                            "enableFloatingIP": "false",
                            "frontendIPConfiguration": {
                                "id": "[variables('lbIPConfig0')]"
                            },
                            "frontendPort": "[parameters('nt0fabricTcpGatewayPort')]",
                            "idleTimeoutInMinutes": "5",
                            "probe": {
                                "id": "[variables('lbProbeID0')]"
                            },
                            "protocol": "Tcp"
                        }
                    },
                    {
                        "name": "LBHttpRule",
                        "properties": {
                            "backendAddressPool": {
                                "id": "[variables('lbPoolID0')]"
                            },
                            "backendPort": "[parameters('nt0fabricHttpGatewayPort')]",
                            "enableFloatingIP": "false",
                            "frontendIPConfiguration": {
                                "id": "[variables('lbIPConfig0')]"
                            },
                            "frontendPort": "[parameters('nt0fabricHttpGatewayPort')]",
                            "idleTimeoutInMinutes": "5",
                            "probe": {
                                "id": "[variables('lbHttpProbeID0')]"
                            },
                            "protocol": "Tcp"
                        }
                    },
                    {
                        "name": "App1",
                        "properties": {
                            "backendAddressPool": {
                                "id": "[variables('lbPoolID0')]"
                            },
                            "backendPort": "[parameters('loadBalancedAppPort1')]",
                            "enableFloatingIP": "false",
                            "frontendIPConfiguration": {
                                "id": "[variables('lbIPConfig0')]"
                            },
                            "frontendPort": "[parameters('loadBalancedAppPort1')]",
                            "idleTimeoutInMinutes": "5",
                            "probe": {
                                "id": "[concat(variables('lbID0'),'/probes/App2')]"
                            },
                            "protocol": "Tcp"
                        }
                    },
                    {
                        "name": "App2",
                        "properties": {
                            "backendAddressPool": {
                                "id": "[variables('lbPoolID0')]"
                            },
                            "backendPort": "[parameters('loadBalancedAppPort2')]",
                            "enableFloatingIP": "false",
                            "frontendIPConfiguration": {
                                "id": "[variables('lbIPConfig0')]"
                            },
                            "frontendPort": "[parameters('loadBalancedAppPort2')]",
                            "idleTimeoutInMinutes": "5",
                            "probe": {
                                "id": "[concat(variables('lbID0'),'/probes/App1')]"
                            },
                            "protocol": "Tcp"
                        }
                    }
                ],
                "probes": [
                    {
                        "name": "FabricGatewayProbe",
                        "properties": {
                            "intervalInSeconds": 5,
                            "numberOfProbes": 2,
                            "port": "[parameters('nt0fabricTcpGatewayPort')]",
                            "protocol": "Tcp"
                        }
                    },
                    {
                        "name": "FabricHttpGatewayProbe",
                        "properties": {
                            "intervalInSeconds": 5,
                            "numberOfProbes": 2,
                            "port": "[parameters('nt0fabricHttpGatewayPort')]",
                            "protocol": "Tcp"
                        }
                    },
                    {
                        "name": "App1",
                        "properties": {
                            "intervalInSeconds": 5,
                            "numberOfProbes": 2,
                            "port": "[parameters('loadBalancedAppPort1')]",
                            "protocol": "Tcp"
                        }
                    },
                    {
                        "name": "App2",
                        "properties": {
                            "intervalInSeconds": 5,
                            "numberOfProbes": 2,
                            "port": "[parameters('loadBalancedAppPort2')]",
                            "protocol": "Tcp"
                        }
                    }
                ]
            }
        },
        {
            "type": "Microsoft.Network/loadBalancers",
            "name": "[concat('LB','-', parameters('clusterName'),'-',parameters('vmNodeType0Name'), '-internal')]",
            "apiVersion": "[variables('lbApiVersion')]",
            "location": "[parameters('computeLocation')]",
            "tags": {
                "resourceType": "Service Fabric",
                "clusterName": "[parameters('clusterName')]"
            },
            "properties": {
                "frontendIPConfigurations": [
                    {
                        "name": "LoadBalancerIPConfig",
                        "properties": {
                            "subnet": {
                                "id": "[variables('subnet0Ref')]"
                            },
                            "privateIPAddress": "[parameters('internalLBAddress')]",
                            "privateIPAllocationMethod": "Static"
                        }
                    }
                ],
                "backendAddressPools": [
                    {
                        "name": "LoadBalancerBEAddressPool",
                        "properties": {}
                    }
                ],
                "loadBalancingRules": [],
                "probes": [],
                "inboundNatPools": [
                    {
                        "name": "LoadBalancerBEAddressNatPool",
                        "properties": {
                            "backendPort": "3389",
                            "frontendIPConfiguration": {
                                "id": "[variables('lbIPConfig0-int')]"
                            },
                            "frontendPortRangeEnd": "4500",
                            "frontendPortRangeStart": "3389",
                            "protocol": "Tcp"
                        }
                    }
                ]
            },
            "dependsOn": [
                "[concat('Microsoft.Network/virtualNetworks/',parameters('virtualNetworkName'))]"
            ]
        },
        {
            "type": "Microsoft.Compute/virtualMachineScaleSets",
            "sku": {
                "name": "[parameters('vmNodeType0Size')]",
                "capacity": "[parameters('nt0InstanceCount')]",
                "tier": "Standard"
            },
            "name": "[parameters('vmNodeType0Name')]",
            "apiVersion": "[variables('vmssApiVersion')]",
            "location": "[parameters('computeLocation')]",
            "tags": {
                "resourceType": "Service Fabric",
                "clusterName": "[parameters('clusterName')]"
            },
            "properties": {
                "overprovision": "[parameters('overProvision')]",
                "upgradePolicy": {
                    "mode": "Automatic"
                },
                "virtualMachineProfile": {
                    "extensionProfile": {
                        "extensions": [
                            {
                                "name": "[concat(parameters('vmNodeType0Name'),'_ServiceFabricNode')]",
                                "properties": {
                                    "type": "ServiceFabricNode",
                                    "autoUpgradeMinorVersion": true,
                                    "protectedSettings": {
                                        "StorageAccountKey1": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('supportLogStorageAccountName')),'2015-05-01-preview').key1]",
                                        "StorageAccountKey2": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('supportLogStorageAccountName')),'2015-05-01-preview').key2]"
                                    },
                                    "publisher": "Microsoft.Azure.ServiceFabric",
                                    "settings": {
                                        "clusterEndpoint": "[reference(parameters('clusterName')).clusterEndpoint]",
                                        "nodeTypeRef": "[parameters('vmNodeType0Name')]",
                                        "dataPath": "D:\\\\SvcFab",
                                        "durabilityLevel": "Silver",
                                        "enableParallelJobs": true,
                                        "nicPrefixOverride": "[parameters('subnet0Prefix')]",
                                        "certificate": {
                                            "thumbprint": "[parameters('certificateThumbprint')]",
                                            "x509StoreName": "[parameters('certificateStoreValue')]"
                                        }
                                    },
                                    "typeHandlerVersion": "1.0"
                                }
                            },
                            {
                                "name": "[concat('VMDiagnosticsVmExt','_vmNodeType0Name')]",
                                "properties": {
                                    "type": "IaaSDiagnostics",
                                    "autoUpgradeMinorVersion": true,
                                    "protectedSettings": {
                                        "storageAccountName": "[parameters('applicationDiagnosticsStorageAccountName')]",
                                        "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('applicationDiagnosticsStorageAccountName')),'2015-05-01-preview').key1]",
                                        "storageAccountEndPoint": "https://core.windows.net/"
                                    },
                                    "publisher": "Microsoft.Azure.Diagnostics",
                                    "settings": {
                                        "WadCfg": {
                                            "DiagnosticMonitorConfiguration": {
                                                "overallQuotaInMB": "50000",
                                                "sinks": "applicationInsights",
                                                "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"
                                                            }
                                                        }
                                                    ]
                                                }
                                            },
                                            "SinksConfig": {
                                                "Sink": [
                                                    {
                                                        "name": "applicationInsights",
                                                        "ApplicationInsights": "[parameters('applicationInsightsKey')]"
                                                    }
                                                ]
                                            }
                                        },
                                        "StorageAccount": "[parameters('applicationDiagnosticsStorageAccountName')]"
                                    },
                                    "typeHandlerVersion": "1.5"
                                }
                            }
                        ]
                    },
                    "networkProfile": {
                        "networkinterfaceConfigurations": [
                            {
                                "name": "[concat(parameters('nicName'), '-0')]",
                                "properties": {
                                    "ipConfigurations": [
                                        {
                                            "name": "[concat(parameters('nicName'),'-',0)]",
                                            "properties": {
                                                "loadBalancerBackendAddressPools": [
                                                    {
                                                        "id": "[variables('lbPoolID0')]"
                                                    },
                                                    {
                                                        "id": "[variables('lbPoolID0-int')]"
                                                    }
                                                ],
                                                "loadBalancerInboundNatPools": [
                                                    {
                                                        "id": "[variables('lbNatPoolID0-int')]"
                                                    }
                                                ],
                                                "subnet": {
                                                    "id": "[variables('subnet0Ref')]"
                                                }
                                            }
                                        }
                                    ],
                                    "primary": true
                                }
                            }
                        ]
                    },
                    "osProfile": {
                        "adminPassword": "[parameters('adminPassword')]",
                        "adminUsername": "[parameters('adminUsername')]",
                        "computernamePrefix": "[parameters('vmNodeType0Name')]",
                        "secrets": [
                            {
                                "sourceVault": {
                                    "id": "[parameters('sourceVaultValue')]"
                                },
                                "vaultCertificates": [
                                    {
                                        "certificateStore": "[parameters('certificateStoreValue')]",
                                        "certificateUrl": "[parameters('certificateUrlValue')]"
                                    }
                                ]
                            }
                        ]
                    },
                    "storageProfile": {
                        "imageReference": {
                            "publisher": "[parameters('vmImagePublisher')]",
                            "offer": "[parameters('vmImageOffer')]",
                            "sku": "[parameters('vmImageSku')]",
                            "version": "[parameters('vmImageVersion')]"
                        },
                        "osDisk": {
                            "caching": "ReadOnly",
                            "createOption": "FromImage",
                            "managedDisk": {
                                "storageAccountType": "Standard_LRS"
                            }
                        }
                    }
                }
            },
            "dependsOn": [
                "[concat('Microsoft.Network/virtualNetworks/', parameters('virtualNetworkName'))]",
                "[concat('Microsoft.Network/loadBalancers/', concat('LB','-', parameters('clusterName'),'-',parameters('vmNodeType0Name')))]",
                "[concat('Microsoft.Storage/storageAccounts/', parameters('applicationDiagnosticsStorageAccountName'))]"
            ]
        },
        {
            "type": "Microsoft.ServiceFabric/clusters",
            "name": "[parameters('clusterName')]",
            "apiVersion": "2017-07-01-preview",
            "location": "[parameters('clusterLocation')]",
            "tags": {
                "resourceType": "Service Fabric",
                "clusterName": "[parameters('clusterName')]"
            },
            "properties": {
                "addonFeatures": [
                    "RepairManager"
                ],
                "certificate": {
                    "thumbprint": "[parameters('certificateThumbprint')]",
                    "x509StoreName": "[parameters('certificateStoreValue')]"
                },
                "clientCertificateCommonNames": [],
                "clientCertificateThumbprints": [],
                "clusterState": "Default",
                "diagnosticsStorageAccountConfig": {
                    "blobEndpoint": "[reference(concat('Microsoft.Storage/storageAccounts/', parameters('supportLogStorageAccountName')), variables('storageApiVersion')).primaryEndpoints.blob]",
                    "protectedAccountKeyName": "StorageAccountKey1",
                    "queueEndpoint": "[reference(concat('Microsoft.Storage/storageAccounts/', parameters('supportLogStorageAccountName')), variables('storageApiVersion')).primaryEndpoints.queue]",
                    "storageAccountName": "[parameters('supportLogStorageAccountName')]",
                    "tableEndpoint": "[reference(concat('Microsoft.Storage/storageAccounts/', parameters('supportLogStorageAccountName')), variables('storageApiVersion')).primaryEndpoints.table]"
                },
                "fabricSettings": [
                    {
                        "parameters": [
                            {
                                "name": "ClusterProtectionLevel",
                                "value": "[parameters('clusterProtectionLevel')]"
                            }
                        ],
                        "name": "Security"
                    }
                ],
                "managementEndpoint": "[concat('https://',parameters('existingStaticIPDnsFQDN'),':',parameters('nt0fabricHttpGatewayPort'))]",
                "nodeTypes": [
                    {
                        "name": "[parameters('vmNodeType0Name')]",
                        "applicationPorts": {
                            "endPort": "[parameters('nt0applicationEndPort')]",
                            "startPort": "[parameters('nt0applicationStartPort')]"
                        },
                        "clientConnectionEndpointPort": "[parameters('nt0fabricTcpGatewayPort')]",
                        "durabilityLevel": "Silver",
                        "ephemeralPorts": {
                            "endPort": "[parameters('nt0ephemeralEndPort')]",
                            "startPort": "[parameters('nt0ephemeralStartPort')]"
                        },
                        "httpGatewayEndpointPort": "[parameters('nt0fabricHttpGatewayPort')]",
                        "isPrimary": true,
                        "vmInstanceCount": "[parameters('nt0InstanceCount')]"
                    }
                ],
                "provisioningState": "Default",
                "reliabilityLevel": "Silver",
                "upgradeMode": "Automatic",
                "vmImage": "Windows"
            },
            "dependsOn": [
                "[concat('Microsoft.Storage/storageAccounts/', parameters('supportLogStorageAccountName'))]"
            ]
        }
    ],
    "outputs": {
        "clusterProperties": {
            "type": "object",
            "value": "[reference(parameters('clusterName'))]"
        }
    }
}

That’s all folks. Have a good one!