Skip to content

Commit 3ff2094

Browse files
jneprzcalvinhzy
andauthored
[SFTP] Initial Preview Release (#8982)
* init managed sftp prototype * port fix * more unit tests * test fixes * clean tests * big clean * fix flake8 * pylint fixes * fix versioning and summary * simplify * sftp cert ests * sftp connect tests * organize tests * support tilde * clean logged out logs * az sftp cert expiration * az sftp cert argument combination tests * parameterize some tests * az sftp connect arg combos * remove batch mode * unit tests * simplify * update args and summary * minor changes and add basic scenario tests * style * remove validators * remove connectivity utils and client factory * remove test-only functions * remove unnecessary wrappers * remove chmod * remove batch commands example * add sftp to service_name.json * Add copyright header test_sftp_scenario.py * Update azext_metadata.json --------- Co-authored-by: Zhiyi Huang <[email protected]>
1 parent 8928792 commit 3ff2094

24 files changed

+3216
-0
lines changed

src/service_name.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,11 @@
539539
"AzureServiceName": "Virtual Machines",
540540
"URL": "https://learn.microsoft.com/azure/virtual-machines/shared-image-galleries"
541541
},
542+
{
543+
"Command": "az sftp",
544+
"AzureServiceName": "Storage",
545+
"URL": "https://learn.microsoft.com/en-us/azure/storage/blobs/secure-file-transfer-protocol-support"
546+
},
542547
{
543548
"Command": "az spatial-anchors-account",
544549
"AzureServiceName": "Mixed Reality",

src/sftp/HISTORY.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.. :changelog:
2+
3+
Release History
4+
===============
5+
6+
1.0.0b1
7+
+++++++
8+
* Initial preview release with SFTP connection and certificate generation support.

src/sftp/README.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Azure CLI SFTP Commands
2+
========================
3+
4+
Secure connections to Azure Storage via SFTP with SSH certificates.
5+
6+
Commands include certificate generation and SFTP connection management.

src/sftp/azext_sftp/__init__.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
# --------------------------------------------------------------------------------------------
5+
6+
"""
7+
Azure CLI SFTP Extension
8+
9+
This extension provides secure SFTP connectivity to Azure Storage Accounts
10+
with automatic Azure AD authentication and certificate management.
11+
12+
Key Features:
13+
- Fully managed SSH certificate generation using Azure AD
14+
- Support for existing SSH keys and certificates
15+
- Interactive and batch SFTP operations
16+
- Automatic credential cleanup for security
17+
- Integration with Azure Storage SFTP endpoints
18+
19+
Commands:
20+
- az sftp cert: Generate SSH certificates for SFTP authentication
21+
- az sftp connect: Connect to Azure Storage Account via SFTP
22+
"""
23+
24+
from azure.cli.core import AzCommandsLoader
25+
26+
from azext_sftp._help import helps # pylint: disable=unused-import
27+
28+
29+
class SftpCommandsLoader(AzCommandsLoader):
30+
"""Command loader for the SFTP extension."""
31+
32+
def __init__(self, cli_ctx=None):
33+
from azure.cli.core.commands import CliCommandType
34+
35+
super().__init__(
36+
cli_ctx=cli_ctx,
37+
custom_command_type=CliCommandType(
38+
operations_tmpl='azext_sftp.custom#{}'))
39+
40+
def load_command_table(self, args):
41+
"""Load the command table for SFTP commands."""
42+
from azext_sftp.commands import load_command_table
43+
load_command_table(self, args)
44+
return self.command_table
45+
46+
def load_arguments(self, command):
47+
"""Load arguments for SFTP commands."""
48+
from azext_sftp._params import load_arguments
49+
load_arguments(self, command)
50+
51+
52+
COMMAND_LOADER_CLS = SftpCommandsLoader

src/sftp/azext_sftp/_help.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# coding=utf-8
2+
# --------------------------------------------------------------------------------------------
3+
# Copyright (c) Microsoft Corporation. All rights reserved.
4+
# Licensed under the MIT License. See License.txt in the project root for license information.
5+
# --------------------------------------------------------------------------------------------
6+
7+
from knack.help_files import helps # pylint: disable=unused-import
8+
9+
10+
helps['sftp'] = """
11+
type: group
12+
short-summary: Generate SSH certificates and access Azure Storage blob data via SFTP
13+
long-summary: |
14+
These commands allow you to generate certificates and connect to Azure Storage Accounts using SFTP.
15+
16+
PREREQUISITES:
17+
- Azure Storage Account with SFTP enabled
18+
- Appropriate RBAC permissions (Storage Blob Data Contributor or similar)
19+
- Azure CLI authentication (az login)
20+
- Network connectivity to Azure Storage endpoints
21+
22+
The SFTP extension provides two main capabilities:
23+
1. Certificate generation using Azure AD authentication (similar to 'az ssh cert')
24+
2. Fully managed SFTP connections to Azure Storage with automatic credential handling
25+
26+
AUTHENTICATION MODES:
27+
- Fully managed: No credentials needed - automatically generates SSH certificate
28+
- Certificate-based: Use existing SSH certificate file
29+
- Key-based: Use SSH public/private key pair (generates certificate automatically)
30+
31+
This extension closely follows the patterns established by the SSH extension.
32+
"""
33+
34+
helps['sftp cert'] = """
35+
type: command
36+
short-summary: Generate SSH certificate for SFTP authentication
37+
long-summary: |
38+
Generate an SSH certificate that can be used for authenticating to Azure Storage SFTP endpoints.
39+
This uses Azure AD authentication to generate a certificate similar to 'az ssh cert'.
40+
41+
CERTIFICATE NAMING:
42+
- Generated certificates have '-aadcert.pub' suffix (e.g., id_rsa-aadcert.pub)
43+
- Certificates are valid for a limited time (typically 1 hour)
44+
- Private keys are generated with 'id_rsa' name when key pair is created
45+
46+
The certificate can be used with 'az sftp connect' or with standard SFTP clients.
47+
examples:
48+
- name: Generate a certificate using an existing public key
49+
text: az sftp cert --public-key-file ~/.ssh/id_rsa.pub --file ~/my_cert.pub
50+
- name: Generate a certificate and create a new key pair in the same directory
51+
text: az sftp cert --file ~/my_cert.pub
52+
- name: Generate a certificate with custom SSH client folder
53+
text: az sftp cert --file ~/my_cert.pub --ssh-client-folder "C:\\Program Files\\OpenSSH"
54+
"""
55+
56+
helps['sftp connect'] = """
57+
type: command
58+
short-summary: Access Azure Storage blob data via SFTP
59+
long-summary: |
60+
Establish an SFTP connection to an Azure Storage Account.
61+
62+
AUTHENTICATION MODES:
63+
1. Fully managed (RECOMMENDED): Run without credentials - automatically generates SSH certificate
64+
and establishes connection. Credentials are cleaned up after use.
65+
66+
2. Certificate-based: Use existing SSH certificate file. Certificate must be generated with
67+
'az sftp cert' or compatible with Azure AD authentication.
68+
69+
3. Key-based: Provide SSH keys - command will generate certificate automatically from your keys.
70+
71+
CONNECTION DETAILS:
72+
- Username format: {storage-account}.{azure-username}
73+
- Port: Uses SSH default (typically 22) unless specified with --port
74+
- Endpoints resolved automatically based on Azure cloud environment:
75+
* Azure Public: {storage-account}.blob.core.windows.net
76+
* Azure China: {storage-account}.blob.core.chinacloudapi.cn
77+
* Azure Government: {storage-account}.blob.core.usgovcloudapi.net
78+
79+
SECURITY:
80+
- Generated credentials are automatically cleaned up after connection
81+
- Temporary files stored in secure temporary directories
82+
- OpenSSH handles certificate validation during connection
83+
examples:
84+
- name: Connect with automatic certificate generation (fully managed - RECOMMENDED)
85+
text: az sftp connect --storage-account mystorageaccount
86+
- name: Connect to storage account with existing certificate
87+
text: az sftp connect --storage-account mystorageaccount --certificate-file ~/my_cert.pub
88+
- name: Connect with existing SSH key pair
89+
text: az sftp connect --storage-account mystorageaccount --public-key-file ~/.ssh/id_rsa.pub --private-key-file ~/.ssh/id_rsa
90+
- name: Connect with custom port
91+
text: az sftp connect --storage-account mystorageaccount --port 2222
92+
- name: Connect with additional SFTP arguments for debugging
93+
text: az sftp connect --storage-account mystorageaccount --sftp-args="-v"
94+
- name: Connect with custom SSH client folder (Windows)
95+
text: az sftp connect --storage-account mystorageaccount --ssh-client-folder "C:\\Program Files\\OpenSSH"
96+
- name: Connect with custom connection timeout
97+
text: az sftp connect --storage-account mystorageaccount --sftp-args="-o ConnectTimeout=30"
98+
"""

src/sftp/azext_sftp/_params.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
# --------------------------------------------------------------------------------------------
5+
# pylint: disable=line-too-long
6+
7+
8+
def load_arguments(self, _):
9+
10+
with self.argument_context('sftp cert') as c:
11+
c.argument('cert_path', options_list=['--file', '-f'],
12+
help='The file path to write the SSH cert to, defaults to public key path with -aadcert.pub appended')
13+
c.argument('public_key_file', options_list=['--public-key-file', '-p'],
14+
help='The RSA public key file path. If not provided, '
15+
'generated key pair is stored in the same directory as --file.')
16+
c.argument('ssh_client_folder', options_list=['--ssh-client-folder'],
17+
help='Folder path that contains ssh executables (ssh-keygen, ssh). '
18+
'Default to ssh executables in your PATH or C:\\Windows\\System32\\OpenSSH on Windows.')
19+
20+
with self.argument_context('sftp connect') as c:
21+
c.argument('storage_account', options_list=['--storage-account', '-s'],
22+
help='Azure Storage Account name for SFTP connection. Must have SFTP enabled.')
23+
c.argument('port', options_list=['--port'],
24+
help='SFTP port. If not specified, uses SSH default port (typically 22).',
25+
type=int)
26+
c.argument('cert_file', options_list=['--certificate-file', '-c'],
27+
help='Path to SSH certificate file for authentication. '
28+
'Must be generated with "az sftp cert" or compatible Azure AD certificate. '
29+
'If not provided, certificate will be generated automatically.')
30+
c.argument('private_key_file', options_list=['--private-key-file', '-i'],
31+
help='Path to RSA private key file. If provided without certificate, '
32+
'a certificate will be generated automatically from this key.')
33+
c.argument('public_key_file', options_list=['--public-key-file', '-p'],
34+
help='Path to RSA public key file. If provided without certificate, '
35+
'a certificate will be generated automatically from this key.')
36+
c.argument('sftp_args', options_list=['--sftp-args'],
37+
help='Additional arguments to pass to the SFTP client. '
38+
'Example: "-v" for verbose output, "-b batchfile.txt" for batch commands, '
39+
'"-o ConnectTimeout=30" for custom timeout.')
40+
c.argument('ssh_client_folder', options_list=['--ssh-client-folder'],
41+
help='Path to folder containing SSH client executables (ssh, sftp, ssh-keygen). '
42+
'Default: Uses executables from PATH or C:\\Windows\\System32\\OpenSSH on Windows.')
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"azext.isPreview": true,
3+
"azext.minCliCoreVersion": "2.75.0"
4+
}

src/sftp/azext_sftp/commands.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
# --------------------------------------------------------------------------------------------
5+
6+
"""Command definitions for the Azure CLI SFTP extension."""
7+
8+
9+
def load_command_table(self, _):
10+
"""Load command table for SFTP extension."""
11+
with self.command_group('sftp') as g:
12+
g.custom_command('cert', 'sftp_cert')
13+
g.custom_command('connect', 'sftp_connect')

src/sftp/azext_sftp/constants.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
# --------------------------------------------------------------------------------------------
5+
6+
from colorama import Fore, Style
7+
8+
# File system constants
9+
WINDOWS_INVALID_FOLDERNAME_CHARS = "\\/*:<>?\"|"
10+
11+
# Default ports
12+
DEFAULT_SSH_PORT = DEFAULT_SFTP_PORT = AZURE_STORAGE_SFTP_PORT = 22
13+
14+
# SSH/SFTP client configuration
15+
SSH_CONNECT_TIMEOUT = 30
16+
SSH_SERVER_ALIVE_INTERVAL = 60
17+
SSH_SERVER_ALIVE_COUNT_MAX = 3
18+
19+
# Certificate and key file naming
20+
SSH_PRIVATE_KEY_NAME = "id_rsa"
21+
SSH_PUBLIC_KEY_NAME = "id_rsa.pub"
22+
SSH_CERT_SUFFIX = "-aadcert.pub"
23+
24+
# Error messages and recommendations
25+
RECOMMENDATION_SSH_CLIENT_NOT_FOUND = (
26+
Fore.YELLOW +
27+
"Ensure OpenSSH is installed correctly.\n"
28+
"Alternatively, use --ssh-client-folder to provide OpenSSH folder path." +
29+
Style.RESET_ALL
30+
)
31+
32+
RECOMMENDATION_STORAGE_ACCOUNT_SFTP = (
33+
Fore.YELLOW +
34+
"Ensure your Azure Storage Account has SFTP enabled.\n"
35+
"Verify your account permissions include Storage Blob Data Contributor or similar." +
36+
Style.RESET_ALL
37+
)

0 commit comments

Comments
 (0)