Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d268733
add sshdcmdargs struct and method to retrieve defaults
tgauth Jul 11, 2025
ebe3308
add default option to get
tgauth Jul 11, 2025
f169871
cleanup get
tgauth Jul 11, 2025
b8356b2
fix merge conflicts
tgauth Jul 18, 2025
0d65d5d
add e2e sshdconfig tests for get/export and update schema
tgauth Jul 18, 2025
2e82fc7
cleanup get/export display
tgauth Jul 21, 2025
812e1cc
update get to read _metadata
tgauth Jul 24, 2025
8bf176d
support custom sshdconfig filepath for get tests
tgauth Jul 24, 2025
b881d7d
update toml
tgauth Jul 24, 2025
894c9b0
add comment to struct
tgauth Jul 24, 2025
011bd8c
fix clippy
tgauth Jul 24, 2025
db2d25a
fix skip logic
tgauth Jul 24, 2025
af54285
fix i8n
tgauth Jul 25, 2025
4695d56
Revert "fix i8n"
tgauth Jul 25, 2025
3acbbdd
fix i8n take 2
tgauth Jul 25, 2025
c72f6a5
fix i8n take 3
tgauth Jul 25, 2025
484003c
use copilot suggestions
tgauth Jul 25, 2025
f5b619f
address Steve's feedback
tgauth Jul 28, 2025
5e2ab58
Update en-us.toml
tgauth Jul 28, 2025
85f9a7b
Merge branch 'main' into add-sshdconfig-get
tgauth Aug 5, 2025
64e1dfc
add check for sshd in test discovery
tgauth Aug 5, 2025
8ad00ad
add newline
tgauth Aug 6, 2025
dba561a
combine export and get command behavior
tgauth Aug 7, 2025
ea05024
update get and export _includeDefaults behavior
tgauth Aug 14, 2025
21da9c0
add _inheritedDefaults functionality
tgauth Aug 14, 2025
11702a0
update parser for match
tgauth Aug 14, 2025
33ab856
modify export behavior
tgauth Aug 14, 2025
2c348ef
add localization
tgauth Aug 14, 2025
d0c61da
fix localization
tgauth Aug 14, 2025
97c767e
Merge branch 'main' into add-sshdconfig-get
tgauth Aug 15, 2025
5474461
Merge branch 'main' into add-sshdconfig-get
tgauth Aug 19, 2025
688e8d3
update tests
tgauth Aug 20, 2025
575f0f8
Merge branch 'main' into add-sshdconfig-get
tgauth Sep 4, 2025
74fb679
address Steve's feedback
tgauth Sep 8, 2025
1f75dff
fix redundant colon
tgauth Sep 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions dsc/tests/dsc_sshdconfig.tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
BeforeDiscovery {
if ($IsWindows) {
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [System.Security.Principal.WindowsPrincipal]::new($identity)
$isElevated = $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
$sshdExists = ($null -ne (Get-Command sshd -CommandType Application -ErrorAction Ignore))
$skipTest = !$isElevated -or !$sshdExists
}
}

Describe 'SSHDConfig resource tests' -Skip:(!$IsWindows -or $skipTest) {
BeforeAll {
# set a non-default value in a temporary sshd_config file
"LogLevel Debug3`nPasswordAuthentication no" | Set-Content -Path $TestDrive/test_sshd_config
$filepath = Join-Path $TestDrive 'test_sshd_config'
$yaml = @"
`$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
metadata:
Microsoft.DSC:
securityContext: elevated
resources:
- name: sshdconfig
type: Microsoft.OpenSSH.SSHD/sshd_config
metadata:
filepath: $filepath
properties:
"@
}

It '<command> works' -TestCases @(
@{ command = 'get' }
@{ command = 'export' }
) {
param($command)
$out = dsc config $command -i "$yaml" | ConvertFrom-Json -Depth 10
$LASTEXITCODE | Should -Be 0
if ($command -eq 'export') {
$out.resources.count | Should -Be 1
$out.resources[0].properties | Should -Not -BeNullOrEmpty
$out.resources[0].properties.port | Should -BeNullOrEmpty
$out.resources[0].properties.passwordAuthentication | Should -Be 'no'
$out.resources[0].properties._inheritedDefaults | Should -BeNullOrEmpty
} else {
$out.results.count | Should -Be 1
$out.results.result.actualState | Should -Not -BeNullOrEmpty
$out.results.result.actualState.port[0] | Should -Be 22
$out.results.result.actualState.passwordAuthentication | Should -Be 'no'
$out.results.result.actualState._inheritedDefaults | Should -Contain 'port'
}
}

It 'Export with filter works' {
$export_yaml = @"
`$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
metadata:
Microsoft.DSC:
securityContext: elevated
resources:
- name: sshdconfig
type: Microsoft.OpenSSH.SSHD/sshd_config
metadata:
filepath: $filepath
properties:
passwordauthentication: 'yes'
"@
$out = dsc config export -i "$export_yaml" | ConvertFrom-Json -Depth 10
$LASTEXITCODE | Should -Be 0
$out.resources.count | Should -Be 1
($out.resources[0].properties.psobject.properties | Measure-Object).count | Should -Be 1
$out.resources[0].properties.passwordAuthentication | Should -Be 'no'
}

It '<command> with _includeDefaults specified works' -TestCases @(
@{ command = 'get'; includeDefaults = $false }
@{ command = 'export'; includeDefaults = $true }
) {
param($command, $includeDefaults)
$filepath = Join-Path $TestDrive 'test_sshd_config'
$input = @"
`$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
metadata:
Microsoft.DSC:
securityContext: elevated
resources:
- name: sshdconfig
type: Microsoft.OpenSSH.SSHD/sshd_config
metadata:
filepath: $filepath
properties:
_includeDefaults: $includeDefaults
"@
$out = dsc config $command -i "$input" | ConvertFrom-Json -Depth 10
$LASTEXITCODE | Should -Be 0
if ($command -eq 'export') {
$out.resources.count | Should -Be 1
$out.resources[0].properties.loglevel | Should -Be 'debug3'
$out.resources[0].properties.port | Should -Be 22
$out.resources[0].properties._inheritedDefaults | Should -BeNullOrEmpty
} else {
$out.results.count | Should -Be 1
($out.results.result.actualState.psobject.properties | Measure-Object).count | Should -Be 2
$out.results.result.actualState.loglevel | Should -Be 'debug3'
$out.results.result.actualState._inheritedDefaults | Should -BeNullOrEmpty
}
}

Context 'Surface a default value that has been set in file' {
BeforeAll {
"Port 22" | Set-Content -Path $TestDrive/test_sshd_config
}

It '<command> works' -TestCases @(
@{ command = 'get' }
@{ command = 'export' }
) {
param($command)
$out = dsc config $command -i "$yaml" | ConvertFrom-Json -Depth 10
$LASTEXITCODE | Should -Be 0
if ($command -eq 'export') {
$out.resources.count | Should -Be 1
$out.resources[0].properties | Should -Not -BeNullOrEmpty
$out.resources[0].properties.port[0] | Should -Be 22
$out.resources[0].properties.passwordauthentication | Should -BeNullOrEmpty
$out.resources[0].properties._inheritedDefaults | Should -BeNullOrEmpty
} else {
$out.results.count | Should -Be 1
$out.results.result.actualState | Should -Not -BeNullOrEmpty
$out.results.result.actualState.port | Should -Be 22
$out.results.result.actualState.passwordAuthentication | Should -Be 'yes'
$out.results.result.actualState._inheritedDefaults | Should -Not -Contain 'port'
}
}
}
}
58 changes: 57 additions & 1 deletion sshdconfig/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions sshdconfig/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ rust-i18n = { version = "3.1" }
schemars = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
tempfile = "3.8"
thiserror = { version = "2.0" }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["ansi", "env-filter", "json"] }
Expand Down
13 changes: 10 additions & 3 deletions sshdconfig/locales/en-us.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
_version = 1

[args]
getInput = "input to get for sshd_config or default shell settings"
exportInput = "input to export from sshd_config"
setInput = "input to set in sshd_config"

[error]
command = "Command"
invalidInput = "Invalid Input"
io = "IO"
json = "JSON"
language = "Language"
notImplemented = "Not Implemented"
parser = "Parser"
parseInt = "Parse Integer"
persist = "Persist"
registry = "Registry"

[get]
Expand All @@ -19,11 +22,11 @@ defaultShellCmdOptionMustBeString = "cmdOption must be a string"
defaultShellEscapeArgsMustBe0Or1 = "'%{input}' must be a 0 or 1"
defaultShellEscapeArgsMustBeDWord = "escapeArguments must be a DWord"
defaultShellMustBeString = "shell must be a string"
notImplemented = "get not yet implemented for Microsoft.OpenSSH.SSHD/sshd_config"
traceInput = "Get input:"
windowsOnly = "Microsoft.OpenSSH.SSHD/Windows is only applicable to Windows"

[main]
export = "Export"
export = "Export command: %{input}"
schema = "Schema command:"
set = "Set command: '%{input}'"

Expand Down Expand Up @@ -51,5 +54,9 @@ shellPathDoesNotExist = "shell path does not exist: '%{shell}'"
shellPathMustNotBeRelative = "shell path must not be relative"

[util]
includeDefaultsMustBeBoolean = "_includeDefaults must be true or false"
inputMustBeEmpty = "get command does not support filtering based on input settings"
sshdConfigNotFound = "sshd_config not found at path: '%{path}'"
sshdConfigReadFailed = "failed to read sshd_config at path: '%{path}'"
sshdElevation = "elevated security context required"
tracingInitError = "Failed to initialize tracing"
11 changes: 8 additions & 3 deletions sshdconfig/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ pub struct Args {

#[derive(Subcommand)]
pub enum Command {
/// Get default shell, eventually to be used for `sshd_config` and repeatable keywords
/// Get default shell and `sshd_config`, eventually to be used for repeatable keywords
Get {
#[clap(short = 'i', long, help = t!("args.getInput").to_string())]
input: Option<String>,
#[clap(short = 's', long, hide = true)]
setting: Setting,
},
Expand All @@ -24,8 +26,11 @@ pub enum Command {
#[clap(short = 'i', long, help = t!("args.setInput").to_string())]
input: String
},
/// Export `sshd_config`
Export,
/// Export `sshd_config`, eventually to be used for repeatable keywords
Export {
#[clap(short = 'i', long, help = t!("args.exportInput").to_string())]
input: Option<String>
},
Schema {
// Used to inform which schema to generate
#[clap(short = 's', long, hide = true)]
Expand Down
7 changes: 5 additions & 2 deletions sshdconfig/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

use rust_i18n::t;
use tempfile::PersistError;
use thiserror::Error;

#[derive(Debug, Error)]
Expand All @@ -10,16 +11,18 @@ pub enum SshdConfigError {
CommandError(String),
#[error("{t}: {0}", t = t!("error.invalidInput"))]
InvalidInput(String),
#[error("{t}: {0}", t = t!("error.io"))]
IOError(#[from] std::io::Error),
#[error("{t}: {0}", t = t!("error.json"))]
Json(#[from] serde_json::Error),
#[error("{t}: {0}", t = t!("error.language"))]
LanguageError(#[from] tree_sitter::LanguageError),
#[error("{t}: {0}", t = t!("error.notImplemented"))]
NotImplemented(String),
#[error("{t}: {0}", t = t!("error.parser"))]
ParserError(String),
#[error("{t}: {0}", t = t!("error.parseInt"))]
ParseIntError(#[from] std::num::ParseIntError),
#[error("{t}: {0}", t = t!("error.persist"))]
PersistError(#[from] PersistError),
#[cfg(windows)]
#[error("{t}: {0}", t = t!("error.registry"))]
RegistryError(#[from] registry_lib::error::RegistryError),
Expand Down
19 changes: 0 additions & 19 deletions sshdconfig/src/export.rs

This file was deleted.

Loading
Loading