Launch an HA ARM template
Overview
You can launch an HA configuration of SFTP Gateway Professional using an Azure Resource Manager (ARM) template. High-availability requires additional resource types like a database, load balancer, and scale set. The ARM template coordinates the provisioning of these resources.
These instructions are for provisioning SFTP Gateway Professional 3.6.0
.
Navigating to the command line interface
In the Azure Portal, you should see the Cloud Shell icon at the top of your screen, to the right of the search bar. Click this to open up the command line console.
You will be prompted to chose between a Bash shell or a PowerShell environment. If it is your first time opening the command line interface, you may be prompted to choose a Storage Account to store any files you create.
Most of the examples in this article will be using Bash.
Create an ARM template
Create a file named sftpgw-arm.json
in the Bash shell with the command:
touch sftpgw-arm.json
Then use your favorite command line text editor to edit the file:
nano sftpgw-arm.json
Here is an article on how to use the Nano text editor.
Paste in the following contents (be careful, it is very long):
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"iprange": {
"type": "string",
"metadata": {
"description": "IP address CIDR for allowed admin access. Use your IP Address followed by /32"
}
},
"linuxAdminUsername": {
"type": "string",
"defaultValue": "ubuntu",
"metadata": {
"description": "Linux admin username"
}
},
"pubKey": {
"type": "string",
"metadata": {
"description": "SSH public key"
}
},
"vmSize": {
"type": "string",
"defaultValue": "Standard_B1s",
"metadata": {
"description": "Size of VM"
}
},
"dbSize": {
"type": "string",
"defaultValue": "Standard_B1ms",
"metadata": {
"description": "Size of DB instance"
}
},
"dbTier": {
"type": "string",
"defaultValue": "Burstable",
"metadata": {
"description": "Tier of DB instance, whether it is burstable, general purpose, or memory optimized"
}
},
"dbAvailabilityZone": {
"type": "int",
"defaultValue": 1,
"metadata": {
"description": "Availability zone for flexible postgresql database"
}
},
"webAdminUsername": {
"type": "string",
"metadata": {
"description": "Optionally, use this parameter and WebAdminPassword to initialize a username to log into web admin interface."
},
"defaultValue": ""
},
"webAdminPassword": {
"type": "string",
"metadata": {
"description": "Optionally, use this parameter and WebAdminUsername to initialize a password to log into web admin interface. This password could show in cloud-init configuration logs. We recommend changing the password after logging in with the admin account to prevent a possible password leak. The password must be at least 12 characters."
},
"defaultValue": ""
},
"randomSeed": {
"type": "string",
"defaultValue": "[newGuid()]"
},
"ipTier": {
"type": "string",
"defaultValue": "Standard",
"metadata": {
"description": "Tier of public IP, defaults to 'Standard' for availability zone spanning"
}
},
"loadBalancerTier": {
"type": "string",
"defaultValue": "Standard",
"metadata": {
"description": "Tier of load balancer, defaults to 'Standard' for availability zone spanning"
}
},
"zones": {
"type": "array",
"defaultValue": [
"1",
"2",
"3"
],
"metadata": {
"description": "Number of availability zones the VMs should span"
}
}
},
"variables": {
"location": "[resourceGroup().location]",
"pubKey": "[parameters('pubKey')]",
"diagStorageAccountName": "[concat('sftpgwdiag', uniqueString(resourceGroup().id, deployment().name, parameters('randomSeed')))]",
"virtualNetworkName": "sftpgw-vnet",
"addressPrefix": "10.0.0.0/16",
"subnetPrefix": "10.0.0.0/24",
"subnetPrivatePrefix": "10.0.1.0/24",
"privateDnsName": "private.postgres.database.azure.com",
"instanceCount": 2,
"dbPassword": "[concat(uniqueString(concat(guid(resourceGroup().id, deployment().name), parameters('randomSeed'))), uniqueString(parameters('randomSeed')))]",
"dbUsername": "sftpgw",
"logGroupName": "logGroup",
"identityName": "sftpgw-instance-identity",
"roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c",
"dbName": "[concat('sftpgw-db-', uniqueString(resourceGroup().id))]",
"dbEndpoint": "[concat(variables('dbName'), '.postgres.database.azure.com')]",
"roleAssignmentName": "[guid(variables('identityName'), variables('roleDefinitionId'), resourceGroup().id, deployment().name)]",
"keyVaultName": "[concat('sftpgw-kv-', uniqueString(resourceGroup().id, deployment().name, parameters('randomSeed')))]",
"keyVaultDbPasswordSecretName": "dbPassword",
"vmNamePrefix": "sftpgw-",
"vmNicPrefix": "sftpgw-nic",
"loadBalancerName": "sftpgw-lb",
"nsgName": "sftpgw-default-nsg",
"scaleSetName": "sftpgw-scale-set",
"ipName": "sftpgw-ip",
"natPoolName": "sftpgw-natpool",
"natStartPort": 2222,
"natEndPort": 2322
},
"resources": [
{
"type": "Microsoft.ManagedIdentity/userAssignedIdentities",
"apiVersion": "2018-11-30",
"name": "[variables('identityName')]",
"location": "[variables('location')]",
"properties": {}
},
{
"type": "Microsoft.KeyVault/vaults",
"apiVersion": "2021-10-01",
"name": "[variables('keyVaultName')]",
"location": "[variables('location')]",
"dependsOn": [
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('identityName'))]"
],
"properties": {
"enabledForDeployment": true,
"enabledForTemplateDeployment": true,
"enabledForDiskEncryption": true,
"tenantId": "[subscription().tenantId]",
"accessPolicies": [
{
"objectId": "[reference(variables('identityName')).principalId]",
"tenantId": "[reference(variables('identityName')).tenantId]",
"permissions": {
"secrets": [
"get",
"list"
]
}
}
],
"sku": {
"name": "standard",
"family": "A"
},
"networkAcls": {
"defaultAction": "Allow",
"bypass": "AzureServices"
}
}
},
{
"type": "Microsoft.KeyVault/vaults/secrets",
"apiVersion": "2021-10-01",
"name": "[format('{0}/{1}', variables('keyVaultName'), variables('keyVaultDbPasswordSecretName'))]",
"properties": {
"value": "[variables('dbPassword')]"
},
"dependsOn": [
"[resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName'))]"
]
},
{
"type": "Microsoft.Network/networkSecurityGroups",
"apiVersion": "2020-11-01",
"name": "[variables('nsgName')]",
"location": "[variables('location')]",
"properties": {
"securityRules": [
{
"name": "admin-ports",
"properties": {
"protocol": "TCP",
"sourcePortRange": "*",
"destinationAddressPrefix": "*",
"access": "Allow",
"priority": 1010,
"direction": "Inbound",
"sourcePortRanges": [],
"destinationPortRanges": [
"80",
"443",
"2222"
],
"sourceAddressPrefixes": [
"[parameters('iprange')]"
],
"destinationAddressPrefixes": []
}
},
{
"name": "default-allow-sftp",
"properties": {
"protocol": "TCP",
"sourcePortRange": "*",
"destinationPortRange": "22",
"sourceAddressPrefix": "*",
"destinationAddressPrefix": "*",
"access": "Allow",
"priority": 1000,
"direction": "Inbound",
"sourcePortRanges": [],
"destinationPortRanges": [],
"sourceAddressPrefixes": [],
"destinationAddressPrefixes": []
}
}
]
}
},
{
"type": "Microsoft.Network/publicIPAddresses",
"apiVersion": "2021-03-01",
"name": "[variables('ipName')]",
"location": "[variables('location')]",
"sku": {
"name": "[parameters('ipTier')]"
},
"properties": {
"publicIPAddressVersion": "IPv4",
"publicIPAllocationMethod": "Static",
"idleTimeoutInMinutes": 4
}
},
{
"type": "Microsoft.Network/virtualNetworks",
"apiVersion": "2020-05-01",
"name": "[variables('virtualNetworkName')]",
"location": "[variables('location')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"subnets": [
{
"name": "default",
"properties": {
"addressPrefix": "[variables('subnetPrefix')]"
}
},
{
"name": "private-1",
"properties": {
"addressPrefix": "[variables('subnetPrivatePrefix')]",
"serviceEndpoints": [
{
"service": "Microsoft.Storage",
"locations": [
"[variables('location')]"
]
}
],
"delegations": [
{
"name": "Microsoft.DBforPostgreSQL.flexibleServers",
"properties": {
"serviceName": "Microsoft.DBforPostgreSQL/flexibleServers"
}
}
],
"privateEndpointNetworkPolicies": "Enabled",
"privateLinkServiceNetworkPolicies": "Enabled"
}
}
]
}
},
{
"type": "Microsoft.Network/virtualNetworks/subnets",
"apiVersion": "2021-03-01",
"name": "[concat(variables('virtualNetworkName'), '/default')]",
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
],
"properties": {
"addressPrefix": "[variables('subnetPrefix')]",
"delegations": [],
"privateEndpointNetworkPolicies": "Enabled",
"privateLinkServiceNetworkPolicies": "Enabled"
}
},
{
"type": "Microsoft.Network/virtualNetworks/subnets",
"apiVersion": "2021-03-01",
"name": "[concat(variables('virtualNetworkName'), '/private-1')]",
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
],
"properties": {
"addressPrefix": "[variables('subnetPrivatePrefix')]",
"serviceEndpoints": [
{
"service": "Microsoft.Storage",
"locations": [
"[variables('location')]"
]
}
],
"delegations": [
{
"name": "Microsoft.DBforPostgreSQL.flexibleServers",
"properties": {
"serviceName": "Microsoft.DBforPostgreSQL/flexibleServers"
}
}
],
"privateEndpointNetworkPolicies": "Enabled",
"privateLinkServiceNetworkPolicies": "Enabled"
}
},
{
"type": "Microsoft.Network/privateDnsZones",
"apiVersion": "2020-01-01",
"name": "[variables('privateDnsName')]",
"location": "global"
},
{
"type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks",
"apiVersion": "2020-06-01",
"name": "[concat(variables('privateDnsName'), '/', format('{0}-link', variables('virtualNetworkName')))]",
"location": "global",
"dependsOn": [
"[resourceId('Microsoft.Network/privateDnsZones', variables('privateDnsName'))]",
"[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
],
"properties": {
"registrationEnabled": false,
"virtualNetwork": {
"id": "[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
}
}
},
{
"type": "Microsoft.DBforPostgreSQL/flexibleServers",
"apiVersion": "2021-06-01",
"name": "[variables('dbName')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('privateDnsName'), format('{0}-link', variables('virtualNetworkName')))]"
],
"sku": {
"name": "[parameters('dbSize')]",
"tier": "[parameters('dbTier')]"
},
"properties": {
"version": "13",
"administratorLogin": "[variables('dbUsername')]",
"administratorLoginPassword": "[variables('dbPassword')]",
"availabilityZone": "[parameters('dbAvailabilityZone')]",
"storage": {
"storageSizeGB": 32
},
"backup": {
"backupRetentionDays": 7,
"geoRedundantBackup": "Disabled"
},
"network": {
"publicNetworkAccess": "Disabled",
"delegatedSubnetResourceId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('virtualNetworkName'), 'private-1')]",
"privateDnsZoneArmResourceId": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateDnsName'))]"
},
"highAvailability": {
"mode": "Disabled"
},
"maintenanceWindow": {
"customWindow": "Disabled",
"dayOfWeek": 0,
"startHour": 0,
"startMinute": 0
}
}
},
{
"type": "Microsoft.DBforPostgreSQL/flexibleServers/configurations",
"apiVersion": "2021-06-01",
"name": "[concat(variables('dbName'), '/azure.extensions')]",
"dependsOn": [
"[resourceId('Microsoft.DBforPostgreSQL/flexibleServers', variables('dbName'))]"
],
"properties": {
"value": "LTREE",
"source": "user-override"
}
},
{
"type": "Microsoft.Network/loadBalancers",
"apiVersion": "2021-03-01",
"name": "[variables('loadBalancerName')]",
"location": "[variables('location')]",
"sku": {
"name": "[parameters('loadBalancerTier')]"
},
"dependsOn": [
"[resourceId('Microsoft.Network/publicIPAddresses', variables('ipName'))]"
],
"properties": {
"frontendIPConfigurations": [
{
"name": "sftpgw-ip",
"properties": {
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('ipName'))]"
}
}
}
],
"backendAddressPools": [
{
"name": "[concat(variables('loadBalancerName'), '-backend-pool')]"
}
],
"outboundRules" : [
{
"name": "networkOut",
"properties": {
"allocatedOutboundPorts": 2,
"protocol": "All",
"enableTcpReset": true,
"idleTimeoutInMinutes": 15,
"backendAddressPool": {
"id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', variables('loadBalancerName'), concat(variables('loadBalancerName'), '-backend-pool'))]"
},
"frontendIPConfigurations": [
{
"id": "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', variables('loadBalancerName'), 'sftpgw-ip')]"
}
]
}
}
],
"loadBalancingRules": [
{
"name": "LBRuleHttp",
"properties": {
"frontendIPConfiguration": {
"id": "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', variables('loadBalancerName'), 'sftpgw-ip')]"
},
"backendAddressPool": {
"id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', variables('loadBalancerName'), concat(variables('loadBalancerName'), '-backend-pool'))]"
},
"protocol": "Tcp",
"frontendPort": 80,
"backendPort": 80,
"enableFloatingIP": false,
"disableOutboundSNAT": true,
"idleTimeoutInMinutes": 30,
"probe": {
"id": "[resourceId('Microsoft.Network/loadBalancers/probes', variables('loadBalancerName'), 'tcpProbe80')]"
}
}
},
{
"name": "LBRuleHttps",
"properties": {
"frontendIPConfiguration": {
"id": "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', variables('loadBalancerName'), 'sftpgw-ip')]"
},
"backendAddressPool": {
"id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', variables('loadBalancerName'), concat(variables('loadBalancerName'), '-backend-pool'))]"
},
"protocol": "Tcp",
"frontendPort": 443,
"backendPort": 443,
"enableFloatingIP": false,
"disableOutboundSNAT": true,
"idleTimeoutInMinutes": 30,
"probe": {
"id": "[resourceId('Microsoft.Network/loadBalancers/probes', variables('loadBalancerName'), 'tcpProbe443')]"
}
}
},
{
"name": "LBRuleSftp",
"properties": {
"frontendIPConfiguration": {
"id": "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', variables('loadBalancerName'), 'sftpgw-ip')]"
},
"backendAddressPool": {
"id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', variables('loadBalancerName'), concat(variables('loadBalancerName'), '-backend-pool'))]"
},
"protocol": "Tcp",
"frontendPort": 22,
"backendPort": 22,
"enableFloatingIP": false,
"disableOutboundSNAT": true,
"idleTimeoutInMinutes": 30,
"probe": {
"id": "[resourceId('Microsoft.Network/loadBalancers/probes', variables('loadBalancerName'), 'tcpProbe443')]"
}
}
}
],
"probes": [
{
"name": "tcpProbe80",
"properties": {
"protocol": "Tcp",
"port": 80,
"intervalInSeconds": 30,
"numberOfProbes": 2
}
},
{
"name": "tcpProbe443",
"properties": {
"protocol": "Tcp",
"port": 443,
"intervalInSeconds": 30,
"numberOfProbes": 2
}
}
],
"inboundNatPools": [
{
"name": "[variables('natPoolName')]",
"properties": {
"frontendIPConfiguration": {
"id": "[concat(resourceId('Microsoft.Network/loadBalancers', variables('loadBalancerName')), '/frontendIPConfigurations/sftpgw-ip')]"
},
"frontendPortRangeStart": "[variables('natStartPort')]",
"frontendPortRangeEnd": "[variables('natEndPort')]",
"backendPort": 2222,
"protocol": "Tcp"
}
}
]
}
},
{
"type": "Microsoft.Authorization/roleAssignments",
"apiVersion": "2020-10-01-preview",
"name": "[variables('roleAssignmentName')]",
"dependsOn": [],
"properties": {
"roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', variables('roleDefinitionId'))]",
"principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('identityName')), '2018-11-30').principalId]",
"principalType": "ServicePrincipal"
}
},
{
"type": "Microsoft.Compute/virtualMachineScaleSets",
"name": "[variables('scaleSetName')]",
"location": "[variables('location')]",
"zones": "[parameters('zones')]",
"apiVersion": "2021-11-01",
"dependsOn": [
"[resourceId('Microsoft.Network/loadBalancers/', variables('loadBalancerName'))]",
"[resourceId('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]",
"[resourceId('Microsoft.DBforPostgreSQL/flexibleServers', variables('dbName'))]"
],
"sku": {
"name": "[parameters('vmSize')]",
"tier": "Standard",
"capacity": "[variables('instanceCount')]"
},
"identity": {
"type": "userAssigned",
"userAssignedIdentities": {
"[resourceID('Microsoft.ManagedIdentity/userAssignedIdentities/',variables('identityName'))]": {}
}
},
"plan": {
"name": "sftpgateway-professional-3-6",
"product": "sftpgateway-professional",
"publisher": "thorntechnologiesllc"
},
"properties": {
"overprovision": "true",
"upgradePolicy": {
"mode": "Manual"
},
"virtualMachineProfile": {
"storageProfile": {
"osDisk": {
"createOption": "FromImage",
"caching": "ReadWrite"
},
"imageReference": {
"publisher": "thorntechnologiesllc",
"offer": "sftpgateway-professional",
"sku": "sftpgateway-professional-3-6",
"version": "latest"
}
},
"userData": "[base64(format('#cloud-config\n repo_update: true\n repo_upgrade: all\n write_files:\n - content: |\n #!/bin/bash\n export CLOUD_PROVIDER=azure\n export ARCHITECTURE=HA\n export LOG_GROUP_NAME={0}\n export SECRET_ID={1}\n export DB_HOST={2}\n export LOAD_BALANCER_ADDRESSES={5}\n path: /opt/sftpgw/launch_config.env\n runcmd:\n - ''[[ -n \"{3}\" && -n \"{4}\" ]] && curl -X \"POST\" \"http://localhost:8080/3.0.0/admin/config\" -H \"accept: */*\" -H \"Content-Type: application/json\" -d \"{{\\\"password\\\": \\\"{3}\\\",\\\"username\\\": \\\"{4}\\\"}}\"''\n', variables('logGroupName'), reference(resourceId('Microsoft.KeyVault/vaults/secrets', variables('keyVaultName'), variables('keyVaultDbPasswordSecretName'))).secretUri, variables('dbEndpoint'), parameters('webAdminPassword'), parameters('webAdminUsername'), reference(variables('ipName')).ipAddress))]",
"osProfile": {
"computerNamePrefix": "[variables('vmNamePrefix')]",
"adminUsername": "[parameters('linuxAdminUsername')]",
"linuxConfiguration": {
"disablePasswordAuthentication": true,
"ssh": {
"publicKeys": [
{
"path": "[concat('/home/', parameters('linuxAdminUsername'), '/.ssh/authorized_keys')]",
"keyData": "[variables('pubKey')]"
}
]
},
"provisionVMAgent": true,
"patchSettings": {
"patchMode": "ImageDefault",
"assessmentMode": "ImageDefault"
}
},
"allowExtensionOperations": true
},
"networkProfile": {
"networkInterfaceConfigurations": [
{
"name": "[variables('vmNicPrefix')]",
"properties": {
"primary": true,
"ipConfigurations": [
{
"name": "ip-config",
"properties": {
"subnet": {
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('virtualNetworkName'), 'default')]"
},
"loadBalancerBackendAddressPools": [
{
"id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', variables('loadBalancerName'), concat(variables('loadBalancerName'), '-backend-pool'))]"
}
],
"loadBalancerInboundNatPools": [
{
"id": "[resourceId('Microsoft.Network/loadBalancers/inboundNatPools/', variables('loadBalancerName'), variables('natPoolName'))]"
}
]
}
}
],
"networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('nsgName'))]"
}
}
}
]
},
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": true,
"storageUri": "[concat('https://', variables('diagStorageAccountName'), '.blob.core.windows.net')]"
}
}
}
}
},
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-09-01",
"name": "[variables('diagStorageAccountName')]",
"location": "[variables('location')]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"properties": {}
},
{
"type": "Microsoft.Insights/autoscaleSettings",
"apiVersion": "2015-04-01",
"name": "autoscalewad",
"location": "[variables('location')]",
"dependsOn": [
"[resourceId('Microsoft.Compute/virtualMachineScaleSets/', variables('scaleSetName'))]"
],
"properties": {
"name": "autoscalewad",
"targetResourceUri": "[concat('/subscriptions/',subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Compute/virtualMachineScaleSets/', variables('scaleSetName'))]",
"enabled": true,
"profiles": [
{
"name": "Profile1",
"capacity": {
"minimum": "2",
"maximum": "10",
"default": "2"
},
"rules": [
{
"metricTrigger": {
"metricName": "Percentage CPU",
"metricResourceUri": "[concat('/subscriptions/',subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Compute/virtualMachineScaleSets/', variables('scaleSetName'))]",
"timeGrain": "PT1M",
"statistic": "Average",
"timeWindow": "PT10M",
"timeAggregation": "Average",
"operator": "GreaterThan",
"threshold": 65
},
"scaleAction": {
"direction": "Increase",
"type": "ChangeCount",
"value": "1",
"cooldown": "PT1M"
}
},
{
"metricTrigger": {
"metricName": "Percentage CPU",
"metricResourceUri": "[concat('/subscriptions/',subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Compute/virtualMachineScaleSets/', variables('scaleSetName'))]",
"timeGrain": "PT1M",
"statistic": "Average",
"timeWindow": "PT10M",
"timeAggregation": "Average",
"operator": "LessThan",
"threshold": 30
},
"scaleAction": {
"direction": "Decrease",
"type": "ChangeCount",
"value": "1",
"cooldown": "PT5M"
}
}
]
}
]
}
}
],
"outputs": {
"publicIP": {
"type": "string",
"value": "[reference(variables('ipName')).ipAddress]"
}
}
}
parameters.json
file
Create a Next, create a file named parameters.json
. This contains all the parameters that you need to pass into the ARM template.
Make sure that you replace the IP and public key values with your own.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"iprange": {
"value": "3.222.237.17/32"
},
"pubKey": {
"value": "ssh-rsa AAAABcdefgw=="
},
"linuxAdminUsername": {
"value": "ubuntu"
},
"vmSize": {
"value": "Standard_D2s_v3"
}
}
}
Note: If you don't have an SSH key pair, you can create one with the following command:
ssh-keygen -t rsa -C private.key -f private.key -q -N ""
This will generate two files:
- private.key: This is the private key, so treat it as you would a password.
- private.key.pub: This is the public key, which you will use for the
pubKey
value.
Deploy the ARM template (using Bash)
Use the following Bash commands to deploy the ARM template. But before you do so, make sure that you replace the following variables:
- GROUP_NAME: This is the name of a Resource Group you are about to create.
- REGION: Specify a region in which to deploy your resources
GROUP_NAME="<your resource group name>"
REGION="West US 2"
TEMPLATE_FILE="sftpgw-arm.json"
az group create --name ${GROUP_NAME} --location "${REGION}"
az group deployment create \
--name "${GROUP_NAME}" \
--resource-group "${GROUP_NAME}" \
--template-file "${TEMPLATE_FILE}" \
--parameters @parameters.json
Deploy the ARM template (using PowerShell)
Use the following PowerShell commands to deploy the ARM template. But before you do so, make sure that you replace the following variables:
- $groupName: This is the name of a Resource Group you are about to create.
- $region: Specify a region in which to deploy your resources
$groupName = "<your resource group name>"
$region = "West US 2"
$templateFile = "sftpgw-arm.json"
$parameterFile="parameters.json"
New-AzureRmResourceGroup -Name $groupName -Location $region
New-AzResourceGroupDeployment `
-ResourceGroupName $groupName `
-TemplateFile $templateFile `
-TemplateParameterFile $parameterFile
How to use the HA stack
The HA configuration of SFTP Gateway operates similarly to a single instance. You can log into the web admin portal and create SFTP users, for example.
There are a few differences worth pointing out to help with initial setup.
How to access the load balancer
The SFTP Gateway HA stack uses a load balancer to distribute traffic to multiple VMs. You can reach the Load Balancer using the Public IP address resource.
When you select this resource, you will see a public IP address.
Paste this into a web browser, and you can go through the First Launch Experience.
Default Cloud Connection and Storage Account
After you have created an admin user and logged into the web admin portal, go to the Settings tab. You will see that the default Cloud Connection is already configured for you.
This points to a Storage Account named sftpgwb89a5...
, with a Container named sftpgateway-default
.
Under the list of resources in the Resource Group, sort the list by Type:
You should see two Storage Accounts, with the following prefixes:
- sftpgw: The default Cloud Connection points to this Storage Account.
- sftpgwdiag: This is used for VM diagnostics.
How to SSH into a VM for troubleshooting
Normally, you would SSH into the VM's public IP over port 2222
. But the HA template operates a little different from what you may be used to.
Each VM does not have a public IP, so you will need to use the Load Balancer's public IP. But rather than using port 2222
, the Load Balancer opens a dynamic port (e.g. 2223
) that maps to port 2222
on a specific VM.
Go to the list of resources in the Resource Group, and look for the Load Balancer:
On the left menu, select Inbound NAT rules.
You will see a list of dynamic ports that map to different VMs.
For example, port 2224
on the Load Balancer maps to port 2222
on Instance 2.