diff --git a/cfbs.json b/cfbs.json index e1f1249..43cba02 100644 --- a/cfbs.json +++ b/cfbs.json @@ -279,6 +279,33 @@ "bundles uninstall_rsh_server", "policy_files services/cfbs/modules/uninstall-rsh-server/uninstall-rsh-server.cf" ] + }, + "windows-capability": { + "description": "Manage and inventory Windows Capabilities.", + "subdirectory": "management/windows-capability", + "steps": [ + "directory ./ services/cfbs/windows-capability/", + "policy_files services/cfbs/windows-capability/", + "bundles windows_capability" + ] + }, + "windows-openssh-server": { + "description": "Optionally install Windows OpenSSH Server.", + "subdirectory": "software/windows", + "steps": [ + "copy windows-openssh-server.cf services/cfbs/software/windows/windows-openssh-server.cf", + "policy_files services/cfbs/software/windows/windows-openssh-server.cf", + "bundles windows_openssh_server" + ] + }, + "windows-optional-feature": { + "description": "Manage and inventory Windows Optional Features.", + "subdirectory": "management/windows-optional-feature", + "steps": [ + "directory ./ services/cfbs/windows-optional-feature/", + "policy_files services/cfbs/windows-optional-feature/", + "bundles windows_optional_feature" + ] } } } diff --git a/management/windows-capability/windows-capability.cf b/management/windows-capability/windows-capability.cf new file mode 100644 index 0000000..739bb23 --- /dev/null +++ b/management/windows-capability/windows-capability.cf @@ -0,0 +1,70 @@ +bundle agent windows_capability_installed(capability_name) +{ + methods: + "Installed" usebundle => windows_capability:_windows_capability_state("${capability_name}", "Installed"); +} +bundle agent windows_optionial_capability_notpresent(capability_name) +{ + methods: + "NotPresent" usebundle => windows_capability:_windows_capability_state("${capability_name}", "NotPresent"); +} + +bundle agent windows_capability +{ + methods: + windows.!data:disable_windows_capability_inventory:: + "windows_capability:_inventory"; +} + +body file control +{ + namespace => "windows_capability"; +} + +# https://learn.microsoft.com/en-us/powershell/module/dism/get-windowscapability?view=windowsserver2022-ps +# https://learn.microsoft.com/en-us/powershell/module/dism/remove-windowscapability?view=windowsserver2022-ps +# https://learn.microsoft.com/en-us/powershell/module/dism/add-windowscapability?view=windowsserver2022-ps +bundle agent _inventory +{ + vars: + windows:: + "cache_file" string => "${sys.statedir}${const.dirsep}${this.namespace}_cache.csv"; + "command" string => "Get-WindowsCapability -Online | ConvertTo-Csv -notypeinformation | select-object -skip 1 | Set-Content -Path '${cache_file}'"; + + "csv" data => readcsv("${cache_file}"); + "i" slist => getindices("csv"); + "${this.namespace}[${i}]" string => "${csv[${i}][0]}:${csv[${i}][1]}", + meta => { "inventory", "attribute_name=Windows Capability" }; + + files: + "${cache_file}" + file_select => default:days_old(1), + delete => default:tidy; + + commands: + windows:: + "${command}" + if => not(fileexists("${cache_file}")), + contain => default:powershell; +} + + +bundle agent _windows_capability_state(capability_name, desired_state) +{ + vars: + "operation" string => ifelse(strcmp("${desired_state}", "Installed"), "Add", "Remove"); + classes: + windows:: + "state_not_ok" expression => returnszero("if((Get-WindowsCapability -Online -Name ${capability_name} | select-object -expandproperty state) -ne '${desired_state}'){exit 0}else{exit 1}", "powershell"); + + commands: + windows.state_not_ok:: + "${operation}-WindowsCapability -Online -Name ${capability_name}" + classes => default:results("bundle", "state"), + contain => default:powershell; + + files: + windows.state_repaired:: + "${_inventory.cache_file}" + delete => default:tidy; +} diff --git a/management/windows-optional-feature/windows-optional-feature.cf b/management/windows-optional-feature/windows-optional-feature.cf new file mode 100644 index 0000000..27aec9b --- /dev/null +++ b/management/windows-optional-feature/windows-optional-feature.cf @@ -0,0 +1,73 @@ +# https://learn.microsoft.com/en-us/powershell/module/dism/get-windowsoptionalfeature?view=windowsserver2022-ps +# https://learn.microsoft.com/en-us/powershell/module/dism/disable-windowsoptionalfeature?view=windowsserver2022-ps +# https://learn.microsoft.com/en-us/powershell/module/dism/enable-windowsoptionalfeature?view=windowsserver2022-ps +# https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/quick-start/enable-hyper-v#enable-hyper-v-using-powershell + +bundle agent windows_optional_feature_enabled( feature_name ) +{ + methods: + "Enabled" usebundle => windows_optional_feature:_promise_state("${feature_name}", "Enabled"); +} + +bundle agent windows_optional_feature_disabled( feature_name ) +{ + methods: + "Disabled" usebundle => windows_optional_feature:_promise_state("${feature_name}", "Disabled"); +} + +bundle agent windows_optional_feature +{ + methods: + windows.!data:disable_windows_optional_feature_inventory:: + "windows_optional_feature:_inventory"; +} + +body file control +{ + namespace => "windows_optional_feature"; +} + +bundle agent _inventory +{ + vars: + windows:: + "cache_file" string => "${sys.statedir}${const.dirsep}${this.namespace}_cache.csv"; + "command" string => "Get-WindowsOptionalFeature -Online | ConvertTo-Csv -notypeinformation | select-object -skip 1 | Set-Content -Path '${cache_file}'"; + "csv" data => readcsv("${cache_file}"); + "i" slist => getindices("csv"); + "${this.namespace}[${i}]" string => "${csv[${i}][0]}:${csv[${i}][1]}", + meta => { "inventory", "attribute_name=Windows Optional Features" }; + + files: + "${cache_file}" + file_select => default:days_old(1), + delete => default:tidy; + + commands: + windows:: + "${command}" + if => not(fileexists("${cache_file}")), + contain => default:powershell; +} + +bundle agent _promise_state(feature_name, desired_state) +{ + vars: + "operation" string => ifelse(strcmp("${desired_state}", "Enabled"), "Enable", "Disable"); + classes: + windows:: + "state_not_ok" expression => returnszero( "if((Get-WindowsOptionalFeature -Online -FeatureName ${feature_name} | select-object -expandproperty state) -ne '${state_name}'){exit 0}else{exit 1}", "powershell"); + commands: + windows.state_not_ok:: +# -All enables parent features if need be + "${operation}-WindowsOptionalFeature -Online -FeatureName ${feature_name} -NoRestart -All" + contain => default:powershell, + classes => default:results("bundle", "state"); +# -NoRestart so that we don't wait forever for a Yes reply from a user +# then if we ran this command due to needing to, restart below. + + files: + windows.state_repaired:: + "${_inventory.cache_file}" + delete => default:tidy; +} diff --git a/software/windows/windows-openssh-server.cf b/software/windows/windows-openssh-server.cf new file mode 100644 index 0000000..ca8a02a --- /dev/null +++ b/software/windows/windows-openssh-server.cf @@ -0,0 +1,29 @@ +bundle agent windows_openssh_server +{ + methods: + data:openssh_server_installed:: + "openssh_server_installed"; +} + +bundle agent windows_openssh_server_installed +{ + methods: + windows:: + "Add OpenSSH.Server Capability" + # NOTE: this version "0.0.1.0" is not the actual version but rather a static number that is mysterious to me. + # the actual installed version will be the "latest" + usebundle => windows_capability_installed("OpenSSH.Server~~~~0.0.1.0"), + classes => classes_generic("openssh"); + + services: + windows.openssh_ok:: + "sshd" + service_policy => "start", + service_method => windows_openssh_service_method; +} + +body service_method windows_openssh_service_method +{ + service_type => "windows"; + service_autostart_policy => "boot_time"; +}