-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Feature/sftp module #28373
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Feature/sftp module #28373
Changes from 3 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
64a4af0
Add SFTP module with Apache License headers
7c0f525
Adding necessary changes to markdown files
ea0d202
Addressing the mismatch in parameter naming
2fe71c8
Update src/Sftp/Sftp/SftpCommands/ConnectAzSftpCommand.cs
DevanshG1 6f728a5
Update src/Sftp/Sftp/Common/FileUtils.cs
DevanshG1 e7ed428
Update src/Sftp/Sftp/Common/SftpUtils.cs
DevanshG1 f8d6fb1
Adding Powershell testing and fixing the format for the markdown files
cdd0e1e
Adding Powershell testing and fixing the format for the markdown files
e16cb85
Minor changes to sftputils
b68f0d6
Changing the test framework for better pipeline coverage
411b44e
Fixing the Namespace issue causing an error with the pipeline testing
16df3a3
Migrating the Test Pipeline to xUnit
8f4fe76
Analyzer fix
61f2424
correcting connect-azsftp markdown file
d69e4b7
Removing dummy keys from tests
dcea400
Resolving comments related to module structure and .proj files
5a93b6c
Removing Unused SDKs
ee1d855
Removing version number from changelog.md
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<!-- | ||
Please leave this section at the top of the change log. | ||
|
||
Changes for the upcoming release should go under the section titled "Upcoming Release", and should adhere to the following format: | ||
|
||
## Upcoming Release | ||
* Overview of change #1 | ||
- Additional information about change #1 | ||
* Overview of change #2 | ||
- Additional information about change #2 | ||
- Additional information about change #2 | ||
* Overview of change #3 | ||
* Overview of change #4 | ||
- Additional information about change #4 | ||
|
||
## YYYY.MM.DD - Version X.Y.Z (Previous Release) | ||
* Overview of change #1 | ||
- Additional information about change #1 | ||
--> | ||
|
||
## Upcoming Release | ||
* Initial release of Az.Sftp module | ||
* Added `New-AzSftpCertificate` cmdlet for generating SSH certificates using Azure AD credentials | ||
- Automatic SSH key pair generation | ||
- Certificate generation for existing public keys | ||
- Support for custom certificate paths | ||
- Cross-platform SSH client detection | ||
* Added `Connect-AzSftp` cmdlet for establishing SFTP connections to Azure Storage accounts | ||
- Fully managed authentication with automatic certificate generation | ||
- Certificate-based authentication using existing SSH certificates | ||
- Key-based authentication with automatic certificate generation | ||
- Support for custom SFTP arguments and SSH client locations | ||
* Support for multiple authentication modes: | ||
- LocalUser parameter for local user authentication | ||
- Interactive authentication (username/password) when using LocalUser | ||
- Enhanced parameter sets for better user experience | ||
* Cross-platform support (Windows, Linux, macOS) | ||
* Comprehensive help documentation following Azure PowerShell standards | ||
* Extensive test suite covering all scenarios | ||
* Security features including secure key handling and short-lived certificates | ||
|
||
## Version 1.0.0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# Release History | ||
DevanshG1 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
## 1.0.0 | ||
|
||
### Features Added | ||
* Initial release of Az.Sftp module | ||
* Added `New-AzSftpCertificate` cmdlet for generating SSH certificates using Azure AD credentials | ||
* Added `Connect-AzSftp` cmdlet for establishing SFTP connections to Azure Storage accounts | ||
* Support for multiple authentication modes: | ||
- Fully managed authentication with automatic certificate generation | ||
- Certificate-based authentication using existing SSH certificates | ||
- Key-based authentication with automatic certificate generation | ||
- LocalUser parameter for local user authentication | ||
* Cross-platform support for Windows, Linux, and macOS | ||
* Comprehensive help documentation and examples | ||
* Integration with Azure PowerShell authentication context | ||
|
||
### Breaking Changes | ||
* N/A - Initial release | ||
|
||
### Bugs Fixed | ||
* N/A - Initial release |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
DevanshG1 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
<PropertyGroup> | ||
<PsModuleName>Sftp</PsModuleName> | ||
</PropertyGroup> | ||
|
||
<!-- Import Azure PowerShell build properties if available --> | ||
<Import Project="$(MSBuildThisFileDirectory)..\..\Az.props" Condition="Exists('$(MSBuildThisFileDirectory)..\..\Az.props')" /> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netstandard2.0</TargetFramework> | ||
<AssemblyName>Microsoft.Azure.PowerShell.Cmdlets.Sftp.Helpers</AssemblyName> | ||
<RootNamespace>Microsoft.Azure.PowerShell.Sftp.Helpers</RootNamespace> | ||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo> | ||
<LangVersion>latest</LangVersion> | ||
<NoWarn>$(NoWarn);CS0108;CS1573;CS1591</NoWarn> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.Azure.Management.Storage" Version="25.0.0" /> | ||
<PackageReference Include="WindowsAzure.Storage" Version="9.3.3" /> | ||
<PackageReference Include="Azure.Storage.Blobs" Version="12.19.1" /> | ||
<PackageReference Include="Microsoft.Azure.Management.Compute" Version="61.0.0" /> | ||
<PackageReference Include="Microsoft.Azure.Management.Network" Version="26.0.0" /> | ||
<PackageReference Update="Microsoft.Rest.ClientRuntime.Azure" Version="3.3.19" /> | ||
<PackageReference Update="Newtonsoft.Json" Version="13.0.3" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Folder Include="Storage\" /> | ||
<Folder Include="Storage\Models\" /> | ||
<Folder Include="Compute\" /> | ||
<Folder Include="Compute\Models\" /> | ||
<Folder Include="Network\" /> | ||
<Folder Include="Network\Models\" /> | ||
<Folder Include="Properties\" /> | ||
</ItemGroup> | ||
|
||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory).., build.proj))\src\Az.Post.props" Condition="Exists('$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory).., build.proj))\src\Az.Post.props')" /> | ||
|
||
</Project> |
250 changes: 250 additions & 0 deletions
250
src/Sftp/Sftp.Test/ScenarioTests/CmdletParameterCompatibilityTests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,250 @@ | ||
using System; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Management.Automation; | ||
using Microsoft.Azure.Commands.Sftp.SftpCommands; | ||
using Microsoft.Azure.Commands.Sftp.Models; | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
|
||
namespace Microsoft.Azure.Commands.Sftp.Test.ScenarioTests | ||
{ | ||
/// <summary> | ||
/// Test suite to ensure PowerShell cmdlet parameters and behavior | ||
/// exactly match Azure CLI command parameters and behavior. | ||
/// Owner: johnli1 | ||
/// </summary> | ||
[TestClass] | ||
public class CmdletParameterCompatibilityTests | ||
{ | ||
[TestMethod] | ||
public void TestNewAzSftpCertificateParametersMatchAzureCLI() | ||
{ | ||
// Test that New-AzSftpCertificate parameters match 'az sftp cert' exactly | ||
|
||
// Arrange | ||
var command = new NewAzSftpCertificateCommand(); | ||
|
||
// Act & Assert - Check that all parameter names and aliases match Azure CLI | ||
|
||
// Azure CLI: --output-file, -o | ||
// PowerShell: -CertificatePath, -OutputFile, -o | ||
Assert.IsNotNull(command.GetType().GetProperty("CertificatePath")); | ||
|
||
// Azure CLI: --public-key-file, -p | ||
// PowerShell: -PublicKeyFile, -p | ||
Assert.IsNotNull(command.GetType().GetProperty("PublicKeyFile")); | ||
|
||
// Azure CLI: --ssh-client-folder | ||
// PowerShell: -SshClientFolder | ||
Assert.IsNotNull(command.GetType().GetProperty("SshClientFolder")); | ||
|
||
// Check that aliases are properly defined | ||
var certPathProp = command.GetType().GetProperty("CertificatePath"); | ||
var aliases = certPathProp?.GetCustomAttributes(typeof(AliasAttribute), false); | ||
Assert.IsNotNull(aliases); | ||
Assert.IsTrue(aliases.Length > 0); | ||
|
||
var aliasAttr = aliases[0] as AliasAttribute; | ||
Assert.IsNotNull(aliasAttr); | ||
Assert.IsTrue(aliasAttr.AliasNames.Any(alias => alias == "OutputFile")); | ||
Assert.IsTrue(aliasAttr.AliasNames.Any(alias => alias == "o")); | ||
} | ||
|
||
[TestMethod] | ||
public void TestConnectAzSftpParametersMatchAzureCLI() | ||
{ | ||
// Test that Connect-AzSftp parameters match 'az sftp connect' exactly | ||
|
||
// Arrange | ||
var command = new ConnectAzSftpCommand(); | ||
|
||
// Act & Assert - Check that all parameter names and aliases match Azure CLI | ||
|
||
// Azure CLI: --storage-account, -s (position 0, required) | ||
// PowerShell: -StorageAccount, -s (position 0, mandatory) | ||
Assert.IsNotNull(command.GetType().GetProperty("StorageAccount")); | ||
|
||
// Azure CLI: --port | ||
// PowerShell: -Port | ||
Assert.IsNotNull(command.GetType().GetProperty("Port")); | ||
|
||
// Azure CLI: --certificate-file, -c | ||
// PowerShell: -CertificateFile, -c | ||
Assert.IsNotNull(command.GetType().GetProperty("CertificateFile")); | ||
|
||
// Azure CLI: --private-key-file, -i | ||
// PowerShell: -PrivateKeyFile, -i | ||
Assert.IsNotNull(command.GetType().GetProperty("PrivateKeyFile")); | ||
|
||
// Azure CLI: --public-key-file, -p | ||
// PowerShell: -PublicKeyFile, -p | ||
Assert.IsNotNull(command.GetType().GetProperty("PublicKeyFile")); | ||
|
||
// Azure CLI: --sftp-args | ||
// PowerShell: -SftpArg | ||
Assert.IsNotNull(command.GetType().GetProperty("SftpArg")); | ||
|
||
// Azure CLI: --ssh-client-folder | ||
// PowerShell: -SshClientFolder | ||
Assert.IsNotNull(command.GetType().GetProperty("SshClientFolder")); | ||
|
||
// Check Parameter attributes match Azure CLI behavior | ||
var storageAccountProp = command.GetType().GetProperty("StorageAccount"); | ||
var parameterAttrs = storageAccountProp?.GetCustomAttributes(typeof(ParameterAttribute), false); | ||
Assert.IsNotNull(parameterAttrs); | ||
Assert.IsTrue(parameterAttrs.Length > 0); | ||
|
||
var parameterAttr = parameterAttrs[0] as ParameterAttribute; | ||
Assert.IsNotNull(parameterAttr); | ||
Assert.IsTrue(parameterAttr.Mandatory); // Required in Azure CLI | ||
Assert.AreEqual(0, parameterAttr.Position); // Position 0 in Azure CLI | ||
} | ||
|
||
[TestMethod] | ||
public void TestHelpTextMatchesAzureCLI() | ||
{ | ||
// Test that help text and descriptions match Azure CLI as closely as possible | ||
|
||
// This test ensures that when users look up help, they see familiar descriptions | ||
// that match what they would see in Azure CLI documentation | ||
|
||
var newCertCommand = new NewAzSftpCertificateCommand(); | ||
var connectCommand = new ConnectAzSftpCommand(); | ||
|
||
// Check that properties have HelpMessage attributes | ||
var certPathProp = newCertCommand.GetType().GetProperty("CertificatePath"); | ||
var paramAttrs = certPathProp?.GetCustomAttributes(typeof(ParameterAttribute), false); | ||
Assert.IsNotNull(paramAttrs); | ||
Assert.IsTrue(paramAttrs.Length > 0); | ||
|
||
var paramAttr = paramAttrs[0] as ParameterAttribute; | ||
Assert.IsNotNull(paramAttr); | ||
Assert.IsFalse(string.IsNullOrEmpty(paramAttr.HelpMessage)); | ||
|
||
// Help message should mention the same concepts as Azure CLI | ||
Assert.IsTrue(paramAttr.HelpMessage.Contains("SSH cert") || | ||
paramAttr.HelpMessage.Contains("certificate")); | ||
} | ||
|
||
[TestMethod] | ||
public void TestOutputTypesMatchAzureCLI() | ||
{ | ||
// Test that cmdlet output types match Azure CLI behavior | ||
|
||
// Azure CLI 'az sftp cert' outputs success/failure messages, no object | ||
var newCertCommand = new NewAzSftpCertificateCommand(); | ||
var outputTypeAttrs = newCertCommand.GetType().GetCustomAttributes(typeof(OutputTypeAttribute), false); | ||
Assert.IsNotNull(outputTypeAttrs); | ||
Assert.IsTrue(outputTypeAttrs.Length > 0); | ||
|
||
var outputTypeAttr = outputTypeAttrs[0] as OutputTypeAttribute; | ||
Assert.IsNotNull(outputTypeAttr); | ||
// Should output PSCertificateInfo containing certificate information | ||
Assert.IsTrue(Array.Exists(outputTypeAttr.Type, t => t.Type == typeof(PSCertificateInfo))); | ||
|
||
// Connect-AzSftp returns a Process object for the SFTP session | ||
var connectCommand = new ConnectAzSftpCommand(); | ||
outputTypeAttrs = connectCommand.GetType().GetCustomAttributes(typeof(OutputTypeAttribute), false); | ||
Assert.IsNotNull(outputTypeAttrs); | ||
Assert.IsTrue(outputTypeAttrs.Length > 0); | ||
|
||
outputTypeAttr = outputTypeAttrs[0] as OutputTypeAttribute; | ||
Assert.IsNotNull(outputTypeAttr); | ||
// Should output Process object for the SFTP session | ||
Assert.IsTrue(Array.Exists(outputTypeAttr.Type, t => t.Type == typeof(System.Diagnostics.Process))); | ||
} | ||
|
||
[TestMethod] | ||
public void TestCmdletVerbsMatchAzureConventions() | ||
{ | ||
// Test that PowerShell verbs follow Azure PowerShell conventions | ||
// while maintaining functional parity with Azure CLI | ||
|
||
// 'az sftp cert' -> 'New-AzSftpCertificate' (New verb for creating) | ||
var newCertCommand = new NewAzSftpCertificateCommand(); | ||
var cmdletAttr = newCertCommand.GetType().GetCustomAttributes(typeof(CmdletAttribute), false)[0] as CmdletAttribute; | ||
Assert.IsNotNull(cmdletAttr); | ||
Assert.AreEqual(VerbsCommon.New, cmdletAttr.VerbName); | ||
Assert.AreEqual("AzSftpCertificate", cmdletAttr.NounName); | ||
|
||
// 'az sftp connect' -> 'Connect-AzSftp' (Connect verb for establishing connection) | ||
var connectCommand = new ConnectAzSftpCommand(); | ||
cmdletAttr = connectCommand.GetType().GetCustomAttributes(typeof(CmdletAttribute), false)[0] as CmdletAttribute; | ||
Assert.IsNotNull(cmdletAttr); | ||
Assert.AreEqual(VerbsCommunications.Connect, cmdletAttr.VerbName); | ||
Assert.AreEqual("AzSftp", cmdletAttr.NounName); | ||
} | ||
|
||
[TestMethod] | ||
public void TestParameterValidationMatchesAzureCLI() | ||
{ | ||
// Test that parameter validation attributes match Azure CLI validation behavior | ||
|
||
var connectCommand = new ConnectAzSftpCommand(); | ||
|
||
// StorageAccount should be validated as not null/empty (like Azure CLI) | ||
var storageAccountProp = connectCommand.GetType().GetProperty("StorageAccount"); | ||
var validationAttrs = storageAccountProp?.GetCustomAttributes(typeof(ValidateNotNullOrEmptyAttribute), false); | ||
Assert.IsNotNull(validationAttrs); | ||
Assert.IsTrue(validationAttrs.Length > 0); | ||
|
||
// Port should be validated if present (like Azure CLI checks for valid port range) | ||
var portProp = connectCommand.GetType().GetProperty("Port"); | ||
Assert.IsNotNull(portProp); | ||
Assert.AreEqual(typeof(int?), portProp.PropertyType); // Should be nullable int | ||
} | ||
|
||
[TestMethod] | ||
public void TestDefaultBehaviorMatchesAzureCLI() | ||
{ | ||
// Test that default parameter behavior matches Azure CLI | ||
|
||
var connectCommand = new ConnectAzSftpCommand(); | ||
|
||
// Port should default to null (Azure CLI uses SSH default of 22) | ||
Assert.IsNull(connectCommand.Port); | ||
|
||
// Certificate, private key, and public key files should default to null | ||
// (Azure CLI auto-generates if not provided) | ||
Assert.IsNull(connectCommand.CertificateFile); | ||
Assert.IsNull(connectCommand.PrivateKeyFile); | ||
Assert.IsNull(connectCommand.PublicKeyFile); | ||
|
||
// SftpArg should default to null (Azure CLI defaults to empty) | ||
Assert.IsNull(connectCommand.SftpArg); | ||
|
||
// SshClientFolder should default to null (Azure CLI auto-detects) | ||
Assert.IsNull(connectCommand.SshClientFolder); | ||
} | ||
|
||
[TestMethod] | ||
public void TestParameterSetLogicMatchesAzureCLI() | ||
{ | ||
// Test that parameter set logic matches Azure CLI's argument validation | ||
|
||
// Azure CLI allows these combinations: | ||
// 1. No auth files (auto-generate everything) | ||
// 2. Certificate file only | ||
// 3. Private key file only (auto-generate cert) | ||
// 4. Public key file only (auto-generate cert) | ||
// 5. Certificate + private key | ||
// 6. Public key + private key (auto-generate cert) | ||
|
||
// PowerShell should handle the same combinations | ||
var connectCommand = new ConnectAzSftpCommand(); | ||
|
||
// All auth parameters should be optional to allow auto-generation | ||
var certFileProp = connectCommand.GetType().GetProperty("CertificateFile"); | ||
var certFileParam = certFileProp?.GetCustomAttributes(typeof(ParameterAttribute), false)[0] as ParameterAttribute; | ||
Assert.IsFalse(certFileParam?.Mandatory ?? true); | ||
|
||
var privateKeyProp = connectCommand.GetType().GetProperty("PrivateKeyFile"); | ||
var privateKeyParam = privateKeyProp?.GetCustomAttributes(typeof(ParameterAttribute), false)[0] as ParameterAttribute; | ||
Assert.IsFalse(privateKeyParam?.Mandatory ?? true); | ||
|
||
var publicKeyProp = connectCommand.GetType().GetProperty("PublicKeyFile"); | ||
var publicKeyParam = publicKeyProp?.GetCustomAttributes(typeof(ParameterAttribute), false)[0] as ParameterAttribute; | ||
Assert.IsFalse(publicKeyParam?.Mandatory ?? true); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.