Enable SFTP on Azure File Share using ARM Template and upload files using WinScp
SFTP is a very widely used protocol which many organizations use today for transferring files within their organization or across organizations. Creating a VM based SFTP is costly and high-maintenance. ACI service is very inexpensive and requires very little maintenance, while data is stored in Azure Files which is a fully managed SMB service in cloud.
This template demonstrates an creating a SFTP server using Azure Container Instances (ACI). The template generates two resources:
- storage account is the storage account used for persisting data, and contains the Azure Files share
- sftp-group is a container group with a mounted Azure File Share. The Azure File Share will provide persistent storage after the container is terminated.
ARM Template for creation of SFTP with New Azure File Share and a new Azure Storage account
Resources.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.4.63.48766",
"templateHash": "17013458610905703770"
}
},
"parameters": {
"storageAccountType": {
"type": "string",
"defaultValue": "Standard_LRS",
"metadata": {
"description": "Storage account type"
},
"allowedValues": [
"Standard_LRS",
"Standard_ZRS",
"Standard_GRS"
]
},
"storageAccountPrefix": {
"type": "string",
"defaultValue": "sftpstg",
"metadata": {
"description": "Prefix for new storage account"
}
},
"fileShareName": {
"type": "string",
"defaultValue": "sftpfileshare",
"metadata": {
"description": "Name of file share to be created"
}
},
"sftpUser": {
"type": "string",
"defaultValue": "sftp",
"metadata": {
"description": "Username to use for SFTP access"
}
},
"sftpPassword": {
"type": "securestring",
"metadata": {
"description": "Password to use for SFTP access"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Primary location for resources"
}
},
"containerGroupDNSLabel": {
"type": "string",
"defaultValue": "[uniqueString(resourceGroup().id, deployment().name)]",
"metadata": {
"description": "DNS label for container group"
}
}
},
"functions": [],
"variables": {
"sftpContainerName": "sftp",
"sftpContainerGroupName": "sftp-group",
"sftpContainerImage": "atmoz/sftp:debian",
"sftpEnvVariable": "[format('{0}:{1}:1001', parameters('sftpUser'), parameters('sftpPassword'))]",
"storageAccountName": "[take(toLower(format('{0}{1}', parameters('storageAccountPrefix'), uniqueString(resourceGroup().id))), 24)]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"name": "[variables('storageAccountName')]",
"location": "[parameters('location')]",
"kind": "StorageV2",
"sku": {
"name": "[parameters('storageAccountType')]"
}
},
{
"type": "Microsoft.Storage/storageAccounts/fileServices/shares",
"apiVersion": "2019-06-01",
"name": "[toLower(format('{0}/default/{1}', variables('storageAccountName'), parameters('fileShareName')))]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
]
},
{
"type": "Microsoft.ContainerInstance/containerGroups",
"apiVersion": "2019-12-01",
"name": "[variables('sftpContainerGroupName')]",
"location": "[parameters('location')]",
"properties": {
"containers": [
{
"name": "[variables('sftpContainerName')]",
"properties": {
"image": "[variables('sftpContainerImage')]",
"environmentVariables": [
{
"name": "SFTP_USERS",
"secureValue": "[variables('sftpEnvVariable')]"
}
],
"resources": {
"requests": {
"cpu": 1,
"memoryInGB": 1
}
},
"ports": [
{
"port": 22,
"protocol": "TCP"
}
],
"volumeMounts": [
{
"mountPath": "[format('/home/{0}/upload', parameters('sftpUser'))]",
"name": "sftpvolume",
"readOnly": false
}
]
}
}
],
"osType": "Linux",
"ipAddress": {
"type": "Public",
"ports": [
{
"port": 22,
"protocol": "TCP"
}
],
"dnsNameLabel": "[parameters('containerGroupDNSLabel')]"
},
"restartPolicy": "OnFailure",
"volumes": [
{
"name": "sftpvolume",
"azureFile": {
"readOnly": false,
"shareName": "[parameters('fileShareName')]",
"storageAccountName": "[variables('storageAccountName')]",
"storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value]"
}
}
]
},
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
]
}
],
"outputs": {
"containerDNSLabel": {
"type": "string",
"value": "[format('{0}.{1}.azurecontainer.io', reference(resourceId('Microsoft.ContainerInstance/containerGroups', variables('sftpContainerGroupName'))).ipAddress.dnsNameLabel, reference(resourceId('Microsoft.ContainerInstance/containerGroups', variables('sftpContainerGroupName')), '2019-12-01', 'full').location)]"
}
}
}
Parameters.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountType": {
"value": "Standard_LRS"
},
"storageAccountPrefix": {
"value": "sftpstg"
},
"fileShareName": {
"value": "sftpfileshare"
},
"sftpUser": {
"value": "sftp"
},
"sftpPassword": {
"value": null
},
"location": {
"value": "[resourceGroup().location]"
},
"containerGroupDNSLabel": {
"value": "[uniqueString(resourceGroup().id, deployment().name)]"
}
}
}
ARM Template to Enable SFTP for an Existing Azure File Share in Azure Storage account
Resources.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.4.63.48766",
"templateHash": "16190402726175806996"
}
},
"parameters": {
"existingStorageAccountResourceGroupName": {
"type": "string",
"metadata": {
"description": "Resource group for existing storage account"
}
},
"existingStorageAccountName": {
"type": "string",
"metadata": {
"description": "Name of existing storage account"
}
},
"existingFileShareName": {
"type": "string",
"metadata": {
"description": "Name of existing file share to be mounted"
}
},
"sftpUser": {
"type": "string",
"defaultValue": "sftp",
"metadata": {
"description": "Username to use for SFTP access"
}
},
"sftpPassword": {
"type": "securestring",
"metadata": {
"description": "Password to use for SFTP access"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Primary location for resources"
}
},
"containerGroupDNSLabel": {
"type": "string",
"defaultValue": "[uniqueString(resourceGroup().id, deployment().name)]",
"metadata": {
"description": "DNS label for container group"
}
}
},
"functions": [],
"variables": {
"sftpContainerName": "sftp",
"sftpContainerGroupName": "sftp-group",
"sftpContainerImage": "atmoz/sftp:debian",
"sftpEnvVariable": "[format('{0}:{1}:1001', parameters('sftpUser'), parameters('sftpPassword'))]"
},
"resources": [
{
"type": "Microsoft.ContainerInstance/containerGroups",
"apiVersion": "2019-12-01",
"name": "[variables('sftpContainerGroupName')]",
"location": "[parameters('location')]",
"properties": {
"containers": [
{
"name": "[variables('sftpContainerName')]",
"properties": {
"image": "[variables('sftpContainerImage')]",
"environmentVariables": [
{
"name": "SFTP_USERS",
"secureValue": "[variables('sftpEnvVariable')]"
}
],
"resources": {
"requests": {
"cpu": 1,
"memoryInGB": 1
}
},
"ports": [
{
"port": 22,
"protocol": "TCP"
}
],
"volumeMounts": [
{
"mountPath": "[format('/home/{0}/upload', parameters('sftpUser'))]",
"name": "sftpvolume",
"readOnly": false
}
]
}
}
],
"osType": "Linux",
"ipAddress": {
"type": "Public",
"ports": [
{
"port": 22,
"protocol": "TCP"
}
],
"dnsNameLabel": "[parameters('containerGroupDNSLabel')]"
},
"restartPolicy": "OnFailure",
"volumes": [
{
"name": "sftpvolume",
"azureFile": {
"readOnly": false,
"shareName": "[parameters('existingFileShareName')]",
"storageAccountName": "[parameters('existingStorageAccountName')]",
"storageAccountKey": "[listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('existingStorageAccountResourceGroupName')), 'Microsoft.Storage/storageAccounts', parameters('existingStorageAccountName')), '2019-06-01').keys[0].value]"
}
}
]
}
}
],
"outputs": {
"containerDNSLabel": {
"type": "string",
"value": "[format('{0}.{1}.azurecontainer.io', reference(resourceId('Microsoft.ContainerInstance/containerGroups', variables('sftpContainerGroupName'))).ipAddress.dnsNameLabel, reference(resourceId('Microsoft.ContainerInstance/containerGroups', variables('sftpContainerGroupName')), '2019-12-01', 'full').location)]"
}
}
}
Parameters.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"existingStorageAccountResourceGroupName": {
"value": null
},
"existingStorageAccountName": {
"value": null
},
"existingFileShareName": {
"value": null
},
"sftpUser": {
"value": "sftp"
},
"sftpPassword": {
"value": null
},
"location": {
"value": "[resourceGroup().location]"
},
"containerGroupDNSLabel": {
"value": "[uniqueString(resourceGroup().id, deployment().name)]"
}
}
}
Deploy the ARM Templates using PowerShell or Azure CLI or Custom Template deployment using Azure Portal.
- Choose the subscription you want to create the sftp service in
- Create a new Resource Group
- It will automatically create a storage account
- Give a File Share Name
- Provide a SFTP user name
- Provide a SFTP password
- Wait till the deployment is done successfully
- Click on the container sftp-group
- Copy the FQDN from the container group
- Download WinScp from WinSCP :: Official Site :: Download
- Provide Hostname : FQDN for ACI; Port Number: 22; User Name and Password
- Click on Login
13. Drag and drop a file from the left side to the Right side.
14. Now, go to the Storage Account and Navigate to File share. The file appears on the file share.
Published on:
Learn moreRelated posts
What’s new with Azure Repos?
We thought it was a good time to check in and highlight some of the work happening in Azure Repos. In this post, we’ve covered several recent ...
Part 1: Building Your First Serverless HTTP API on Azure with Azure Functions & FastAPI
Introduction This post is Part 1 of the series Serverless Application Development with Azure Functions and Azure Cosmos DB, where we explore ...
Announcing GPT 5.2 Availability in Azure for U.S. Government Secret and Top Secret Clouds
Today, we are excited to announce that GPT-5.2, Azure OpenAI’s newest frontier reasoning model, is available in Microsoft Azure for U.S. Gover...
Sync data from Dynamics 365 Finance & Operations Azure SQL Database (Tier2) to local SQL Server (AxDB)
A new utility to synchronize data from D365FO cloud environments to local AxDB, featuring incremental sync and smart strategies.
Azure Cosmos DB Conf 2026 — Call for Proposals Is Now Open
Every production system has a story behind it. The scaling limit you didn’t expect. The data model that finally clicked. The tradeoff you had ...