diff --git a/.github/actions/spelling/expect/generic_terms.txt b/.github/actions/spelling/expect/generic_terms.txt index 5180255f..83d6f776 100644 --- a/.github/actions/spelling/expect/generic_terms.txt +++ b/.github/actions/spelling/expect/generic_terms.txt @@ -1,21 +1,29 @@ -AKV +wildcards +ssh Amd -Authenticode -automerge +usr +screenshots currentstate +Scrollbars +Searchbox +VGpu +versioning +worktree +sortby +msft +automerge +Workaround +keyvalue +psobject +webcam +websites +workaround +AKV +Authenticode esrp gtm -msft NPH Peet rfc -screenshots -Scrollbars -Searchbox SFP Signtool -sortby -ssh -usr -versioning -VGpu \ No newline at end of file diff --git a/resources/Help/Microsoft.Windows.Setting.Privacy/Microsoft.Windows.Setting.Privacy.md b/resources/Help/Microsoft.Windows.Setting.Privacy/Microsoft.Windows.Setting.Privacy.md new file mode 100644 index 00000000..e274d0c8 --- /dev/null +++ b/resources/Help/Microsoft.Windows.Setting.Privacy/Microsoft.Windows.Setting.Privacy.md @@ -0,0 +1,65 @@ +--- +external help file: Microsoft.Windows.Setting.Privacy.psm1-Help.xml +Module Name: Microsoft.Windows.Setting.Privacy +ms.date: 11/04/2024 +online version: +schema: 2.0.0 +title: Privacy +--- + +# Privacy + +## SYNOPSIS + +The `Privacy` DSC Resource allows you to manage Windows privacy settings. This resource ensures that the specified privacy settings are in the desired state on your local machine. + +## DESCRIPTION + +The `Privacy` DSC Resource allows you to manage Windows privacy settings. This resource ensures that the specified privacy settings are in the desired state on your local machine. + +## PARAMETERS + +| **Parameter** | **Attribute** | **DataType** | **Description** | **Allowed Values** | +| ---------------------------------- | ------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | +| `SID` | Key | String | The security identifier. This is a key property and should not be set manually. | N/A | +| `EnablePersonalizedAds` | Optional | Boolean | Indicates whether personalized ads should be enabled. The default value is `$null`. You can find the setting in `Settings > Privacy & security > General -> Let apps show me personalized ads by using my advertising ID. | `$true`, `$false` | | +| `EnableLocalContentByLanguageList` | Optional | Boolean | Indicates whether local content by language list should be enabled. The default value is `$null`. You can find the setting in `Settings > Privacy & security > General -> Let Windows provide locally relevant content based on my language list. | `$true`, `$false` | | +| `EnableAppLaunchTracking` | Optional | Boolean | Indicates whether app launch tracking should be enabled. The default value is `$null`. You can find the setting in `Settings > Privacy & security > General -> Let Windows improve Start and search results by tracking app launches. | `$true`, `$false` | | +| `ShowContentSuggestion` | Optional | Boolean | Indicates whether content suggestions should be shown. The default value is `$null`. You can find the setting in `Settings > Privacy & security > General -> Show me suggested content in the Settings app. | `$true`, `$false` | | +| `EnableAccountNotifications` | Optional | Boolean | Indicates whether account notifications should be enabled. The default value is `$null`. You can find the setting in `Settings > Privacy & security > General -> Show me notifications in the Settings app. | `$true`, `$false` | | + + +## EXAMPLES + +### EXAMPLE 1 - Set the personalized ads on + +```powershell +PS C:\> $params = @{# + EnablePersonalizedAds = $true +} +PS C:\> Invoke-DscResource -Name Privacy -Method Set -Property $params -ModuleName Microsoft.Windows.Setting.Privacy + +# This enables personalized ads for the specified user. +``` + +### Example 2 - Disable app launch tracking + +```powershell +PS C:\> $params = @{ + EnableAppLaunchTracking = $false + } +PS C:\> Invoke-DscResource -Name Privacy -Method Set -Property $params -ModuleName Microsoft.Windows.Setting.Privacy + +# This disables app launch tracking for the specified user. +``` + +### Example 3 - Turn on content suggestion + +```powershell +PS C:\> $params = @{ + ShowContentSuggestion = $true + } +PS C:\> Invoke-DscResource -Name Privacy -Method Set -Property $params -ModuleName Microsoft.Windows.Setting.Privacy + +This enables content suggestions for the specified user. +``` diff --git a/resources/Microsoft.Windows.Setting.Privacy/Microsoft.Windows.Setting.Privacy.psd1 b/resources/Microsoft.Windows.Setting.Privacy/Microsoft.Windows.Setting.Privacy.psd1 new file mode 100644 index 00000000..2a4b9b10 --- /dev/null +++ b/resources/Microsoft.Windows.Setting.Privacy/Microsoft.Windows.Setting.Privacy.psd1 @@ -0,0 +1,132 @@ +# +# Module manifest for module 'Microsoft.Windows.Setting.Privacy' +# +# Generated by: Microsoft Corporation +# +# Generated on: 11/14/2024 +# + +@{ + + # Script module or binary module file associated with this manifest. + RootModule = 'Microsoft.Windows.Setting.Privacy.psm1' + + # Version number of this module. + ModuleVersion = '0.1.0' + + # Supported PSEditions + # CompatiblePSEditions = @() + + # ID used to uniquely identify this module + GUID = '151d9b2f-4c29-4920-b75f-a220fdb24888' + + # Author of this module + Author = 'Microsoft Corporation' + + # Company or vendor of this module + CompanyName = 'Microsoft Corporation' + + # Copyright statement for this module + Copyright = '(c) Microsoft Corporation. All rights reserved.' + + # Description of the functionality provided by this module + Description = 'DSC Resource for Windows Settings Privacy' + + # Minimum version of the PowerShell engine required by this module + PowerShellVersion = '7.2' + + # Name of the PowerShell host required by this module + # PowerShellHostName = '' + + # Minimum version of the PowerShell host required by this module + # PowerShellHostVersion = '' + + # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + # DotNetFrameworkVersion = '' + + # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + # ClrVersion = '' + + # Processor architecture (None, X86, Amd64) required by this module + # ProcessorArchitecture = '' + + # Modules that must be imported into the global environment prior to importing this module + # RequiredModules = @() + + # Assemblies that must be loaded prior to importing this module + # RequiredAssemblies = @() + + # Script files (.ps1) that are run in the caller's environment prior to importing this module. + # ScriptsToProcess = @() + + # Type files (.ps1xml) to be loaded when importing this module + # TypesToProcess = @() + + # Format files (.ps1xml) to be loaded when importing this module + # FormatsToProcess = @() + + # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess + # NestedModules = @() + + # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. + # FunctionsToExport = @() + + # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. + # CmdletsToExport = @() + + # Variables to export from this module + # VariablesToExport = @() + + # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. + # AliasesToExport = @() + + # DSC resources to export from this module + DscResourcesToExport = @('Privacy') + + # List of all modules packaged with this module + # ModuleList = @() + + # List of all files packaged with this module + # FileList = @() + + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. + PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() + + # A URL to the license for this module. + LicenseUri = 'https://github.com/microsoft/winget-dsc/blob/main/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/microsoft/winget-dsc' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + # Prerelease string of this module + Prerelease = 'alpha' + + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false + + # External dependent modules of this module + # ExternalModuleDependencies = @() + + } # End of PSData hashtable + + } # End of PrivateData hashtable + + # HelpInfo URI of this module + # HelpInfoURI = '' + + # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. + # DefaultCommandPrefix = '' + +} + diff --git a/resources/Microsoft.Windows.Setting.Privacy/Microsoft.Windows.Setting.Privacy.psm1 b/resources/Microsoft.Windows.Setting.Privacy/Microsoft.Windows.Setting.Privacy.psm1 new file mode 100644 index 00000000..78a3e1aa --- /dev/null +++ b/resources/Microsoft.Windows.Setting.Privacy/Microsoft.Windows.Setting.Privacy.psm1 @@ -0,0 +1,271 @@ +if ([string]::IsNullOrEmpty($env:TestRegistryPath)) { + # $global:webcamConsentStorePath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\webcam' + $global:advertisingInfoPath = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\AdvertisingInfo' + $global:userProfilePath = 'HKCU:\Control Panel\International\User Profile' + $global:advancedProfilePath = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced' + $global:contentDeliveryManagerPath = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' + $global:accountNotificationsPath = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\SystemSettings\AccountNotifications' +} else { + $global:advertisingInfoPath = $env:TestRegistryPath +} + +#region Functions +function DoesRegistryKeyPropertyExist { + param ( + [Parameter(Mandatory)] + [string]$Path, + + [Parameter(Mandatory)] + [string]$Name + ) + + # Get-ItemProperty will return $null if the registry key property does not exist. + $itemProperty = Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue + + if ($itemProperty) { + return $itemProperty.$Name + } +} + +function GetPropertyMapping { + $inputObject = [System.Collections.Generic.List[PSCustomObject]]::new() + + $inputObject.Add([PSCustomObject]@{ + Name = 'EnablePersonalizedAds' + Key = 'Enabled' + Expression = 1 + Path = $global:advertisingInfoPath + }) + + $inputObject.Add([PSCustomObject]@{ + Name = 'EnableLocalContentByLanguageList' + Key = 'HttpAcceptLanguageOptOut' + Expression = 0 + Path = $global:userProfilePath + }) + + $inputObject.Add([PSCustomObject]@{ + Name = 'EnableAppLaunchTracking' + Key = 'Start_TrackProgs' + Expression = 1 + Path = $global:advancedProfilePath + }) + + $inputObject.Add([PSCustomObject]@{ + Name = 'ShowContentSuggestion' + Key = @('SubscribedContent-338393Enabled', 'SubscribedContent-353694Enabled', 'SubscribedContent-353696Enabled') + Expression = 1 + Path = $global:contentDeliveryManagerPath + }) + + $inputObject.Add([PSCustomObject]@{ + Name = 'EnableAccountNotifications' + Key = 'EnableAccountNotifications' + Expression = 1 + Path = $global:accountNotificationsPath + }) + + return $inputObject +} + +function GetPrivacyState { + [CmdletBinding()] + [OutputType([Privacy])] + param ( + [Parameter()] + [AllowNull()] + [hashtable] $SettableProperties + ) + + $currentState = [Privacy]::New() + $mapping = GetPropertyMapping + + foreach ($p in $SettableProperties.GetEnumerator()) { + $property = $mapping | Where-Object { $_.Name -eq $p.Key } + $expression = $property.Expression + $path = $property.Path + + if ($null -ne $property) { + # wondering why we check for array? This is because the HttpAcceptLanguageOptOut key is buggy + $keyValue = if ($property.Key -is [array]) { + $property.Key | ForEach-Object { DoesRegistryKeyPropertyExist -Path $path -Name $_ } + } else { + DoesRegistryKeyPropertyExist -Path $path -Name $property.Key + } + + if (-not ([string]::IsNullOrEmpty($keyValue))) { + [bool]$setter = ($keyValue -eq $expression) + + $currentState.$($p.Key) = $setter + } else { + $currentState.$($p.Key) = $true + } + } + } + + return $currentState +} + +function TestPrivacyState { + [CmdletBinding()] + [OutputType([bool])] + param ( + [Parameter(Mandatory = $true)] + [hashtable] $CurrentState, + + [Parameter(Mandatory = $true)] + [Privacy] $TestState + ) + + $res = $true + $CurrentState.GetEnumerator() | ForEach-Object { + $testValue = $TestState.psobject.properties[$_.Key].Value + if ($null -ne $testValue) { + if ($testValue -ne $_.Value) { + $res = $false + } + } + } + + return $res +} + +function SetPrivacyState { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [Privacy] $PrivacyState + ) + + $mapping = GetPropertyMapping + + $props = $PrivacyState.psobject.properties | Where-Object { $null -ne $_.Value } + + foreach ($p in $props) { + $map = $mapping | Where-Object { $_.Name -eq $p.Name } + if (-not (Test-Path $map.Path)) { + New-Item -Path $map.Path -Force | Out-Null + } + + foreach ($key in $map.Key) { + $value = ($p.Value -as [int]) + if ($map.Key -eq 'HttpAcceptLanguageOptOut') { + $value = ($value -eq 0) ? 1 : 0 + } + + if (-not (DoesRegistryKeyPropertyExist -Path $map.Path -Name $key)) { + New-ItemProperty -Path $map.Path -Name $key -Value $value -PropertyType DWord + } + + Set-ItemProperty -Path $map.Path -Name $key -Value $value -Force + } + } +} +#endregion Functions + +#region Classes +#region DSCResources +<# +.SYNOPSIS + The `Privacy` DSC Resource allows you to manage Windows privacy settings. This resource ensures that the specified privacy settings are in the desired state on your local machine. + +.PARAMETER SID + The security identifier. This is a key property and should not be set manually. + +.PARAMETER EnablePersonalizedAds + Indicates whether personalized ads should be enabled. The default value is `$null`. You can find the setting in `Settings > Privacy & security > General -> Let apps show me personalized ads by using my advertising ID. + +.PARAMETER EnableLocalContentByLanguageList + Indicates whether local content by language list should be enabled. The default value is `$null`. You can find the setting in `Settings > Privacy & security > General -> Let Windows provide locally relevant content based on my language list. + +.PARAMETER EnableAppLaunchTracking + Indicates whether app launch tracking should be enabled. The default value is `$null`. You can find the setting in `Settings > Privacy & security > General -> Let Windows improve Start and search results by tracking app launches. + +.PARAMETER ShowContentSuggestion + Indicates whether content suggestions should be shown. The default value is `$null`. You can find the setting in `Settings > Privacy & security > General -> Show me suggested content in the Settings app. + +.PARAMETER EnableAccountNotifications + Indicates whether account notifications should be enabled. The default value is `$null`. You can find the setting in `Settings > Privacy & security > General -> Show me notifications in the Settings app. + +.EXAMPLE + PS C:\> $params = @{ + EnablePersonalizedAds = $true + } + PS C:\> Invoke-DscResource -Name Privacy -Method Set -Property $params -ModuleName Microsoft.Windows.Setting.Privacy + + This enables personalized ads for the specified user. + +.EXAMPLE + PS C:\> $params = @{ + EnableAppLaunchTracking = $false + } + PS C:\> Invoke-DscResource -Name Privacy -Method Set -Property $params -ModuleName Microsoft.Windows.Setting.Privacy + + This disables app launch tracking for the specified user. + +.EXAMPLE + PS C:\> $params = @{ + ShowContentSuggestion = $true + } + PS C:\> Invoke-DscResource -Name Privacy -Method Set -Property $params -ModuleName Microsoft.Windows.Setting.Privacy + + This enables content suggestions for the specified user. +#> +[DSCResource()] +class Privacy { + # Key required. Do not set. + [DscProperty(Key)] + [string] $SID + + [DscProperty()] + [nullable[bool]] $EnablePersonalizedAds + + [DscProperty()] + [nullable[bool]] $EnableLocalContentByLanguageList + + [DscProperty()] + [nullable[bool]] $EnableAppLaunchTracking + + [DscProperty()] + [nullable[bool]] $ShowContentSuggestion + + [DscProperty()] + [nullable[bool]] $EnableAccountNotifications + + [Privacy] Get() { + $currentState = GetPrivacyState -SettableProperties $this.ToHashTable() + return $currentState + } + + [bool] Test() { + $currentState = $this.Get() + $result = TestPrivacyState -CurrentState $currentState.ToHashTable() -TestState $this + return $result + } + + [void] Set() { + if (-not $this.Test()) { + SetPrivacyState -PrivacyState $this + } + } + + #region Privacy helper functions + static [bool] GetPersonalizedAdsStatus() { + $keyValue = TryGetRegistryValue -Key $global:advertisingInfoPath -Property 'Enabled' + return ($keyvalue -eq 1) + } + + [hashtable] ToHashTable() { + $parameters = @{} + foreach ($property in $this.PSObject.Properties) { + if (-not ([string]::IsNullOrEmpty($property.Value))) { + $parameters[$property.Name] = $property.Value + } + } + + return $parameters + } + + #endRegion Privacy helper functions +} +#endRegion classes diff --git a/tests/Microsoft.Windows.Setting.Privacy/Microsoft.Windows.Setting.Privacy.Tests.ps1 b/tests/Microsoft.Windows.Setting.Privacy/Microsoft.Windows.Setting.Privacy.Tests.ps1 new file mode 100644 index 00000000..58b7bcc5 --- /dev/null +++ b/tests/Microsoft.Windows.Setting.Privacy/Microsoft.Windows.Setting.Privacy.Tests.ps1 @@ -0,0 +1,104 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +using module Microsoft.Windows.Setting.Privacy + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest + +<# +.Synopsis + Pester tests related to the Microsoft.Windows.Setting.Privacy PowerShell module. +#> + +BeforeAll { + $psDesiredStateModuleVersion = (Get-Module -ListAvailable -Name PSDesiredStateConfiguration).Version + if ($null -eq $psDesiredStateModuleVersion) { + Install-Module -Name PSDesiredStateConfiguration -Force -SkipPublisherCheck + } + + Import-Module Microsoft.Windows.Setting.Privacy + $params = @{ + EnablePersonalizedAds = $true + EnableLocalContentByLanguageList = $true + EnableAppLaunchTracking = $true + ShowContentSuggestion = $true + EnableAccountNotifications = $true + } + + $currentState = Invoke-DscResource -Name Privacy -ModuleName Microsoft.Windows.Setting.Privacy -Method Get -Property $params + + $global:Parameters = $currentState.ToHashTable() + + Write-Verbose ("Current state: `n{0}" -f $($global:Parameters | ConvertTo-Json | Out-String)) -Verbose +} + +Describe 'List available DSC resources' { + It 'Shows DSC Resources' { + $expectedDSCResources = 'Privacy' + $availableDSCResources = (Get-DscResource -Module Microsoft.Windows.Setting.Privacy).Name + $availableDSCResources.count | Should -Be 1 + $availableDSCResources | Where-Object { $expectedDSCResources -notcontains $_ } | Should -BeNullOrEmpty -ErrorAction Stop + } +} + +Describe 'Privacy' { + It 'Sets personalized ads' -Skip:(!$IsWindows) { + $desiredState = @{ + EnablePersonalizedAds = $true + } + + Invoke-DscResource -Name Privacy -ModuleName Microsoft.Windows.Setting.Privacy -Method Set -Property $desiredState + + $finalState = Invoke-DscResource -Name Privacy -ModuleName Microsoft.Windows.Setting.Privacy -Method Get -Property $desiredState + $finalState.EnablePersonalizedAds | Should -BeTrue + } + + It 'Sets websites relevant content by access language list' -Skip:(!$IsWindows) { + $desiredState = @{ + EnableLocalContentByLanguageList = $true + } + + Invoke-DscResource -Name Privacy -ModuleName Microsoft.Windows.Setting.Privacy -Method Set -Property $desiredState + + $finalState = Invoke-DscResource -Name Privacy -ModuleName Microsoft.Windows.Setting.Privacy -Method Get -Property $desiredState + $finalState.EnableLocalContentByLanguageList | Should -BeTrue + } + + It 'Sets improve start and search result by tracking app launches' -Skip:(!$IsWindows) { + $desiredState = @{ + EnableAppLaunchTracking = $true + } + + Invoke-DscResource -Name Privacy -ModuleName Microsoft.Windows.Setting.Privacy -Method Set -Property $desiredState + + $finalState = Invoke-DscResource -Name Privacy -ModuleName Microsoft.Windows.Setting.Privacy -Method Get -Property $desiredState + $finalState.EnableAppLaunchTracking | Should -BeTrue + } + + It 'Sets suggestion content in settings app' -Skip:(!$IsWindows) { + $desiredState = @{ + ShowContentSuggestion = $true + } + + Invoke-DscResource -Name Privacy -ModuleName Microsoft.Windows.Setting.Privacy -Method Set -Property $desiredState + + $finalState = Invoke-DscResource -Name Privacy -ModuleName Microsoft.Windows.Setting.Privacy -Method Get -Property $desiredState + $finalState.ShowContentSuggestion | Should -BeTrue + } + + It 'Sets the show notifications in settings app' -Skip:(!$IsWindows) { + $desiredState = @{ + EnableAccountNotifications = $true + } + + Invoke-DscResource -Name Privacy -ModuleName Microsoft.Windows.Setting.Privacy -Method Set -Property $desiredState + + $finalState = Invoke-DscResource -Name Privacy -ModuleName Microsoft.Windows.Setting.Privacy -Method Get -Property $desiredState + $finalState.EnableAccountNotifications | Should -BeTrue + } +} + +AfterAll { + Write-Verbose -Message 'Restoring the machine to the original state.' -Verbose + Invoke-DscResource -Name Privacy -ModuleName Microsoft.Windows.Setting.Privacy -Method Set -Property $global:Parameters +} diff --git a/utilities/tools/New-DscResourceModule.ps1 b/utilities/tools/New-DscResourceModule.ps1 index 5d2e9222..8a4ddf8c 100644 --- a/utilities/tools/New-DscResourceModule.ps1 +++ b/utilities/tools/New-DscResourceModule.ps1 @@ -68,7 +68,7 @@ $moduleManifestParams = @{ FunctionsToExport = @() CmdletsToExport = @() VariablesToExport = @() - AliasesToExport = @() + AliasesToExport = @('*') # Export all aliases as a workaround for a bug in New-ModuleManifest. See: https://github.com/PowerShell/PSDesiredStateConfiguration/issues/101 LicenseUri = 'https://github.com/microsoft/winget-dsc/blob/main/LICENSE' ProjectUri = 'https://github.com/microsoft/winget-dsc' Prerelease = 'alpha'