├── Indented.SecurityPolicy ├── enum │ ├── Enabled.ps1 │ ├── Ensure.ps1 │ ├── SecurityOptions │ │ ├── ForceGuest.ps1 │ │ ├── LdapServerIntegrity.ps1 │ │ ├── DontDisplayLockedUserId.ps1 │ │ ├── LdapClientIntegrity.ps1 │ │ ├── SmbServerNameHardeningLevel.ps1 │ │ ├── RestrictSendingNTLMTraffic.ps1 │ │ ├── ForceKeyProtection.ps1 │ │ ├── ScRemoveOption.ps1 │ │ ├── NoConnectedUser.ps1 │ │ ├── RestrictReceivingNTLMTraffic.ps1 │ │ ├── AllocateDASD.ps1 │ │ ├── AuditReceivingNTLMTraffic.ps1 │ │ ├── NTLMMinSec.ps1 │ │ ├── ConsentPromptBehaviorUser.ps1 │ │ ├── LmCompatibilityLevel.ps1 │ │ ├── SupportedEncryptionTypes.ps1 │ │ ├── ConsentPromptBehaviorAdmin.ps1 │ │ ├── RestrictNTLMInDomain.ps1 │ │ └── AuditNTLMInDomain.ps1 │ └── RegistryValueType.ps1 ├── class │ ├── SecurityOptions │ │ ├── AccountStatusGuest.ps1 │ │ ├── RenameAccountGuest.ps1 │ │ ├── AccountStatusAdministrator.ps1 │ │ ├── RenameAccountAdministrator.ps1 │ │ ├── RenameAccount.ps1 │ │ ├── AccountStatus.ps1 │ │ └── AccountBase.ps1 │ ├── SecurityPolicy │ │ ├── LSA_ENUMERATION_INFORMATION.cs │ │ ├── LsaPolicyAccess.cs │ │ ├── SID_NAME_USE.cs │ │ ├── LSA_UNICODE_STRING.cs │ │ ├── LSA_OBJECT_ATTRIBUTES.cs │ │ ├── Properties │ │ │ └── AssemblyInfo.cs │ │ ├── Account.cs │ │ ├── Win32SecurityIdentifier.cs │ │ ├── SecurityPolicy.csproj │ │ ├── ServiceAccount.cs │ │ ├── UnsafeNativeMethods.cs │ │ └── UserRight.cs │ ├── SecurityPolicy.sln │ └── Dsc │ │ ├── GroupManagedServiceAccount.ps1 │ │ ├── SecurityOption.ps1 │ │ ├── RegistryPolicy.ps1 │ │ └── UserRightAssignment.ps1 ├── private │ ├── CloseLsaPolicy.ps1 │ ├── NewImplementingType.ps1 │ ├── GetMachineSid.ps1 │ ├── RenameIadsLocalUser.ps1 │ ├── DisableIadsLocalUser.ps1 │ ├── EnableIadsLocalUser.ps1 │ ├── GetSecurityOptionData.ps1 │ ├── OpenLsaPolicy.ps1 │ ├── ImportUserRightData.ps1 │ ├── GetIadsLocalUser.ps1 │ └── ImportSecurityOptionData.ps1 ├── public │ ├── Install-GroupManagedServiceAccount.ps1 │ ├── Test-GroupManagedServiceAccount.ps1 │ ├── Uninstall-GroupManagedServiceAccount.ps1 │ ├── Clear-UserRight.ps1 │ ├── Get-AssignedUserRight.ps1 │ ├── Grant-UserRight.ps1 │ ├── Reset-SecurityOption.ps1 │ ├── Get-UserRight.ps1 │ ├── Resolve-UserRight.ps1 │ ├── Revoke-UserRight.ps1 │ ├── Resolve-SecurityOption.ps1 │ ├── Get-SecurityOption.ps1 │ ├── Set-UserRight.ps1 │ └── Set-SecurityOption.ps1 ├── InitializeModule.ps1 ├── test │ ├── private │ │ ├── GetIadsLocalUser.tests.ps1 │ │ ├── RenameIadsLocalUser.tests.ps1 │ │ ├── EnableIadsLocalUser.tests.ps1 │ │ ├── DisableIadsLocalUser.tests.ps1 │ │ ├── ImportUserRightData.tests.ps1 │ │ └── ImportSecurityOptionData.tests.ps1 │ ├── public │ │ ├── Resolve-UserRight.tests.ps1 │ │ ├── Get-AssignedUserRight.tests.ps1 │ │ ├── Resolve-SecurityOption.tests.ps1 │ │ ├── Get-UserRight.tests.ps1 │ │ ├── Grant-UserRight.tests.ps1 │ │ ├── Revoke-UserRight.tests.ps1 │ │ ├── Get-SecurityOption.tests.ps1 │ │ ├── Clear-UserRight.tests.ps1 │ │ ├── Reset-SecurityOption.tests.ps1 │ │ └── Set-UserRight.tests.ps1 │ ├── securityoptions │ │ ├── RenameAccount.tests.ps1 │ │ └── AccountStatus.tests.ps1 │ └── dsc │ │ ├── GroupManagedServiceAccount.tests.ps1 │ │ └── SecurityOption.tests.ps1 ├── help │ ├── Clear-UserRight.md │ ├── Install-GroupManagedServiceAccount.md │ ├── Uninstall-GroupManagedServiceAccount.md │ ├── Test-GroupManagedServiceAccount.md │ ├── Get-SecurityOption.md │ ├── Get-AssignedUserRight.md │ ├── Get-UserRight.md │ ├── Resolve-UserRight.md │ ├── Resolve-SecurityOption.md │ ├── Reset-SecurityOption.md │ ├── Grant-UserRight.md │ ├── Set-UserRight.md │ ├── Set-SecurityOption.md │ └── Revoke-UserRight.md ├── en │ ├── userRights.psd1 │ └── about_UserRights.txt ├── Indented.SecurityPolicy.psm1 ├── Indented.SecurityPolicy.format.ps1xml └── Indented.SecurityPolicy.psd1 ├── LICENSE ├── appveyor.yml ├── readme.md └── .gitignore /Indented.SecurityPolicy/enum/Enabled.ps1: -------------------------------------------------------------------------------- 1 | enum Enabled { 2 | Disabled 3 | Enabled 4 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/Ensure.ps1: -------------------------------------------------------------------------------- 1 | enum Ensure { 2 | Absent 3 | Present 4 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/ForceGuest.ps1: -------------------------------------------------------------------------------- 1 | enum ForceGuest { 2 | Classic 3 | GuestOnly 4 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/LdapServerIntegrity.ps1: -------------------------------------------------------------------------------- 1 | enum LdapServerIntegrity { 2 | None 3 | RequireSigning 4 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/DontDisplayLockedUserId.ps1: -------------------------------------------------------------------------------- 1 | enum DontDisplayLockedUserId { 2 | All 3 | DisplayNameOnly 4 | None 5 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/LdapClientIntegrity.ps1: -------------------------------------------------------------------------------- 1 | enum LdapClientIntegrity { 2 | None 3 | NegotiateSigning 4 | RequireSigning 5 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/SmbServerNameHardeningLevel.ps1: -------------------------------------------------------------------------------- 1 | enum SmbServerNameHardeningLevel { 2 | Off 3 | Accept 4 | Required 5 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/RestrictSendingNTLMTraffic.ps1: -------------------------------------------------------------------------------- 1 | enum RestrictSendingNTLMTraffic { 2 | AllowAll 3 | AuditAll 4 | DenyAll 5 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/ForceKeyProtection.ps1: -------------------------------------------------------------------------------- 1 | enum ForceKeyProtection { 2 | UserInputNotRequired 3 | PromptOnFirstUse 4 | PromptAlways 5 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/ScRemoveOption.ps1: -------------------------------------------------------------------------------- 1 | enum ScRemoveOption { 2 | NoAction 3 | LockWorkstation 4 | ForceLogoff 5 | Disconnect 6 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/NoConnectedUser.ps1: -------------------------------------------------------------------------------- 1 | enum NoConnectedUser { 2 | Disabled = 0 3 | DenyAdd = 1 4 | DenyAddAndLogon = 3 5 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/RestrictReceivingNTLMTraffic.ps1: -------------------------------------------------------------------------------- 1 | enum RestrictReceivingNTLMTraffic { 2 | AllowAll 3 | DenyDomainAccounts 4 | DenyAll 5 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/RegistryValueType.ps1: -------------------------------------------------------------------------------- 1 | enum RegistryValueType { 2 | DWord 3 | QWord 4 | String 5 | MultiString 6 | ExpandString 7 | Binary 8 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/AllocateDASD.ps1: -------------------------------------------------------------------------------- 1 | enum AllocateDASD { 2 | Administrators 3 | AdministratorsAndPowerUsers 4 | AdministratorsAndInteractiveUsers 5 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/AuditReceivingNTLMTraffic.ps1: -------------------------------------------------------------------------------- 1 | enum AuditReceivingNTLMTraffic { 2 | Disable 3 | EnableForDomainAccounts 4 | EnableForAllAccounts 5 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/NTLMMinSec.ps1: -------------------------------------------------------------------------------- 1 | [Flags()] 2 | enum NTLMMinSec { 3 | RequireNTLMv2SessionSecurity = 524288 4 | Require128BitEncryption = 536870912 5 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityOptions/AccountStatusGuest.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Security.Principal 2 | 3 | class AccountStatusGuest : AccountStatus { 4 | [WellKnownSidType]$SidType = 'AccountGuestSid' 5 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityOptions/RenameAccountGuest.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Security.Principal 2 | 3 | class RenameAccountGuest : RenameAccount { 4 | [WellKnownSidType]$SidType = 'AccountGuestSid' 5 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityOptions/AccountStatusAdministrator.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Security.Principal 2 | 3 | class AccountStatusAdministrator : AccountStatus { 4 | [WellKnownSidType]$SidType = 'AccountAdministratorSid' 5 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityOptions/RenameAccountAdministrator.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Security.Principal 2 | 3 | class RenameAccountAdministrator : RenameAccount { 4 | [WellKnownSidType]$SidType = 'AccountAdministratorSid' 5 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/ConsentPromptBehaviorUser.ps1: -------------------------------------------------------------------------------- 1 | enum ConsentPromptBehaviorUser { 2 | AutomaticallyDeny = 0 3 | PromptForCredentialsOnSecureDesktop = 1 4 | PromptForCredentials = 3 5 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/LmCompatibilityLevel.ps1: -------------------------------------------------------------------------------- 1 | enum LmCompatibilityLevel { 2 | SendLMAndNTLMResponses 3 | SendLMAndNTLM 4 | SendNTLMResponseOnly 5 | SendNTLMv2ResponseOnly 6 | SendNTLMv2ResponseOnlyRefuseLM 7 | SendNTLMv2ResponseOnlyRefuseLMAndNTLM 8 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/SupportedEncryptionTypes.ps1: -------------------------------------------------------------------------------- 1 | [Flags()] 2 | enum SupportedEncryptionTypes { 3 | DES_CBC_CRC = 1 4 | DES_CBC_MD5 = 2 5 | RC4_HMAC_MD5 = 4 6 | AES128_HMAC_SHA1 = 8 7 | AES256_HMAC_SHA1 = 16 8 | Future = 2147483616 9 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/ConsentPromptBehaviorAdmin.ps1: -------------------------------------------------------------------------------- 1 | enum ConsentPromptBehaviorAdmin { 2 | NoPrompting 3 | PromptForCredentialsOnSecureDesktop 4 | PromptForConsentOnSecureDesktop 5 | PromptForCredentials 6 | PromptForConsent 7 | PromptForConsentForNonWindowsBinaries 8 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/RestrictNTLMInDomain.ps1: -------------------------------------------------------------------------------- 1 | enum RestrictNTLMInDomain { 2 | Disable = 0 3 | DenyDomainAccountsToDomainServers = 1 4 | DenyDomainAccounts = 3 5 | DenyDomainServers = 5 6 | DenyAll = 7 7 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityPolicy/LSA_ENUMERATION_INFORMATION.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Indented.SecurityPolicy 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | internal struct LSA_ENUMERATION_INFORMATION 8 | { 9 | public IntPtr Sid; 10 | } 11 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityPolicy/LsaPolicyAccess.cs: -------------------------------------------------------------------------------- 1 | namespace Indented.SecurityPolicy 2 | { 3 | internal enum LsaPolicyAccess : int 4 | { 5 | POLICY_READ = 0x20006, 6 | POLICY_ALL_ACCESS = 0x00F0FFF, 7 | POLICY_EXECUTE = 0X20801, 8 | POLICY_WRITE = 0X207F8 9 | } 10 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/enum/SecurityOptions/AuditNTLMInDomain.ps1: -------------------------------------------------------------------------------- 1 | enum AuditNTLMInDomain { 2 | Disable = 0 3 | EnableForDomainAccountsToDomainServers = 1 4 | EnableForDomainAccounts = 3 5 | EnableForDomainServers = 5 6 | EnableAll = 7 7 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/private/CloseLsaPolicy.ps1: -------------------------------------------------------------------------------- 1 | function CloseLsaPolicy { 2 | <# 3 | .SYNOPSIS 4 | Close the LSA policy handle if it is open. 5 | .DESCRIPTION 6 | Close the LSA policy handle if it is open. 7 | #> 8 | 9 | [CmdletBinding()] 10 | param ( 11 | [AllowNull()] 12 | [Object]$lsa 13 | ) 14 | 15 | if ($lsa) { 16 | $lsa.Dispose() 17 | } 18 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityPolicy/SID_NAME_USE.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Indented.SecurityPolicy 4 | { 5 | internal enum SID_NAME_USE 6 | { 7 | SidTypeUser = 1, 8 | SidTypeGroup, 9 | SidTypeDomain, 10 | SidTypeAlias, 11 | SidTypeWellKnownGroup, 12 | SidTypeDeletedAccount, 13 | SidTypeInvalid, 14 | SidTypeUnknown, 15 | SidTypeComputer 16 | } 17 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/private/NewImplementingType.ps1: -------------------------------------------------------------------------------- 1 | function NewImplementingType { 2 | <# 3 | .SYNOPSIS 4 | A short helper to create a named type. 5 | .DESCRIPTION 6 | A short helper to create a named type. Persent to help mocking. 7 | #> 8 | 9 | [CmdletBinding()] 10 | param ( 11 | [Parameter(Mandatory)] 12 | [String]$Name 13 | ) 14 | 15 | ($Name -as [Type])::new() 16 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityPolicy/LSA_UNICODE_STRING.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Indented.SecurityPolicy 5 | { 6 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 7 | internal struct LSA_UNICODE_STRING 8 | { 9 | public ushort Length; 10 | public ushort MaximumLength; 11 | [MarshalAs(UnmanagedType.LPWStr)] 12 | public string Buffer; 13 | } 14 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityPolicy/LSA_OBJECT_ATTRIBUTES.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Indented.SecurityPolicy 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | internal struct LSA_OBJECT_ATTRIBUTES 8 | { 9 | public int Length; 10 | public IntPtr RootDirectory; 11 | public IntPtr ObjectName; 12 | public int Attributes; 13 | public IntPtr SecurityDescriptor; 14 | public IntPtr SecurityQualityOfService; 15 | } 16 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/private/GetMachineSid.ps1: -------------------------------------------------------------------------------- 1 | function GetMachineSid { 2 | <# 3 | .SYNOPSIS 4 | Get the SID of the current machine. 5 | .DESCRIPTION 6 | Get the SID of the current machine. 7 | 8 | The current machine SID should not be confused with a SID used by Active Directory. 9 | #> 10 | 11 | [CmdletBinding()] 12 | [OutputType([System.Security.Principal.SecurityIdentifier])] 13 | param ( ) 14 | 15 | [Indented.SecurityPolicy.Account]::LookupAccountName($env:COMPUTERNAME, $env:COMPUTERNAME) 16 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityOptions/RenameAccount.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Security.Principal 2 | 3 | class RenameAccount : AccountBase { 4 | [String]$Value 5 | 6 | [RenameAccount] Get() { 7 | $this.Value = (GetIadsLocalUser -Sid $this.GetSid()).Name 8 | 9 | return $this 10 | } 11 | 12 | [Void] Set() { 13 | RenameIadsLocalUser -Sid $this.GetSid() -NewName $this.Value 14 | } 15 | 16 | [Boolean] Test() { 17 | $localUser = GetIadsLocalUser -Sid $this.GetSid() 18 | 19 | return $localUser.Name -eq $this.Value 20 | } 21 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityOptions/AccountStatus.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Security.Principal 2 | 3 | class AccountStatus : AccountBase { 4 | [Enabled]$Value 5 | 6 | [AccountStatus] Get() { 7 | $localUser = GetIadsLocalUser -Sid $this.GetSid() 8 | $this.Value = [Enabled][Int]$localUser.Enabled 9 | 10 | return $this 11 | } 12 | 13 | [Void] Set() { 14 | if ([Boolean][Int]$this.Value) { 15 | EnableIadsLocalUser -Sid $this.GetSid() 16 | } else { 17 | DisableIadsLocalUser -Sid $this.GetSid() 18 | } 19 | } 20 | 21 | [Boolean] Test() { 22 | $localUser = GetIadsLocalUser -Sid $this.GetSid() 23 | 24 | return $localUser.Enabled -eq ([Boolean][Int]$this.Value) 25 | } 26 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityOptions/AccountBase.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Security.Principal 2 | 3 | class AccountBase { 4 | [WellKnownSidType]$SidType 5 | 6 | [SecurityIdentifier]$MachineSid = (GetMachineSid) 7 | 8 | [SecurityIdentifier] GetSid() { 9 | $domainRole = (Get-CimInstance Win32_ComputerSystem -Property DomainRole).DomainRole 10 | if ($domainRole -in 4, 5) { 11 | $searcher = [ADSISearcher]'(objectClass=domainDNS)' 12 | $null = $searcher.PropertiesToLoad.Add('objectSID') 13 | $domainSid = [SecurityIdentifier]::new($searcher.FindOne().Properties['objectSID'][0], 0) 14 | 15 | return [SecurityIdentifier]::new($this.SidType, $domainSid) 16 | } else { 17 | return [SecurityIdentifier]::new($this.SidType, $this.MachineSid) 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/public/Install-GroupManagedServiceAccount.ps1: -------------------------------------------------------------------------------- 1 | using namespace Indented.SecurityPolicy 2 | 3 | function Install-GroupManagedServiceAccount { 4 | <# 5 | .SYNOPSIS 6 | Adds a Group Managed Service Account to the local machine. 7 | .DESCRIPTION 8 | Adds a Group Managed Service Account to the local machine. 9 | .EXAMPLE 10 | Install-GroupManagedServiceAccount -AccountName domain\name$ 11 | #> 12 | 13 | [CmdletBinding()] 14 | param ( 15 | # The name of the Group Managed Service Account. 16 | [Parameter(Mandatory, Position = 1, ValueFromPipeline, ValueFromPipelineByPropertyName)] 17 | [String]$AccountName 18 | ) 19 | 20 | process { 21 | try { 22 | [ServiceAccount]::AddServiceAccount($AccountName) 23 | } catch { 24 | $pscmdlet.ThrowTerminatingError($_) 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/private/RenameIadsLocalUser.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Security.Principal 2 | 3 | function RenameIadsLocalUser { 4 | <# 5 | .SYNOPSIS 6 | ADSI based alternative to Rename-LocalUser. 7 | .DESCRIPTION 8 | This simple ADSI-based version of Rename-LocalUser allows use the RenameAccount security option on PowerShell Core. 9 | #> 10 | 11 | [CmdletBinding()] 12 | param ( 13 | # The SID of a user. 14 | [Parameter(Mandatory, Position = 1, ValueFromPipelineByPropertyName)] 15 | [SecurityIdentifier]$Sid, 16 | 17 | # The new name of the user. 18 | [Parameter(Mandatory)] 19 | [String]$NewName 20 | ) 21 | 22 | process { 23 | $iadsUser = GetIadsLocalUser -Sid $Sid -AsIadsUser 24 | 25 | if ($iadsUser.Get('Name') -ne $NewName) { 26 | $iadsUser.Rename($NewName) 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/public/Test-GroupManagedServiceAccount.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Management.Automation 2 | 3 | function Test-GroupManagedServiceAccount { 4 | <# 5 | .SYNOPSIS 6 | Test whether or not a Group Managed Service Account is installed in the NetLogon store. 7 | .DESCRIPTION 8 | Test whether or not a Group Managed Service Account is installed in the NetLogon store. 9 | #> 10 | 11 | [CmdletBinding()] 12 | [OutputType([Boolean])] 13 | param ( 14 | # The name of the Group Managed Service Account. 15 | [Parameter(Mandatory, Position = 1, ValueFromPipeline, ValueFromPipelineByPropertyName)] 16 | [String]$AccountName 17 | ) 18 | 19 | process { 20 | try { 21 | return [Indented.SecurityPolicy.ServiceAccount]::IsServiceAccount($AccountName) 22 | } catch { 23 | $pscmdlet.ThrowTerminatingError($_) 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/private/DisableIadsLocalUser.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Security.Principal 2 | 3 | function DisableIadsLocalUser { 4 | <# 5 | .SYNOPSIS 6 | ADSI based alternative to Disable-LocalUser. 7 | .DESCRIPTION 8 | This simple ADSI-based version of Disable-LocalUser allows use the AccountStatus security option on PowerShell Core. 9 | #> 10 | 11 | [CmdletBinding()] 12 | param ( 13 | # The SID of a user. 14 | [Parameter(Mandatory, Position = 1, ValueFromPipelineByPropertyName)] 15 | [SecurityIdentifier]$Sid 16 | ) 17 | 18 | process { 19 | $iadsUser = GetIadsLocalUser -Sid $Sid -AsIadsUser 20 | 21 | $userFlags = $iadsUser.Get('userFlags') 22 | if (($userFlags -band 2) -eq 0) { 23 | $iadsUser.Put( 24 | 'userFlags', 25 | $userFlags -bor 2 26 | ) 27 | $iadsUser.SetInfo() 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/private/EnableIadsLocalUser.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Security.Principal 2 | using namespace System.Security.Principal 3 | 4 | function EnableIadsLocalUser { 5 | <# 6 | .SYNOPSIS 7 | ADSI based alternative to Enable-LocalUser. 8 | .DESCRIPTION 9 | This simple ADSI-based version of Enable-LocalUser allows use the AccountStatus security option on PowerShell Core. 10 | #> 11 | 12 | [CmdletBinding()] 13 | param ( 14 | # The SID of a user. 15 | [Parameter(Mandatory, Position = 1, ValueFromPipelineByPropertyName)] 16 | [SecurityIdentifier]$Sid 17 | ) 18 | 19 | process { 20 | $iadsUser = GetIadsLocalUser -Sid $Sid -AsIadsUser 21 | 22 | $userFlags = $iadsUser.Get('userFlags') 23 | if (($userFlags -band 2) -eq 2) { 24 | $iadsUser.Put( 25 | 'userFlags', 26 | $userFlags -bxor 2 27 | ) 28 | $iadsUser.SetInfo() 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Indented Automation 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Indented.SecurityPolicy/private/GetSecurityOptionData.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Management.Automation 2 | 3 | function GetSecurityOptionData { 4 | <# 5 | .SYNOPSIS 6 | Get option data for the named security option. 7 | .DESCRIPTION 8 | Get option data for the named security option. 9 | #> 10 | 11 | [CmdletBinding()] 12 | param ( 13 | # The name of the security option. 14 | [Parameter(Mandatory)] 15 | [String]$Name 16 | ) 17 | 18 | if ($Script:securityOptionData.Contains($Name)) { 19 | $securityOptionData = $Script:securityOptionData[$Name] 20 | if (-not $securityOptionData.Name) { 21 | $securityOptionData.Name = $Name 22 | } 23 | $securityOptionData 24 | } else { 25 | $errorRecord = [ErrorRecord]::new( 26 | [ArgumentException]::new('{0} is an invalid security option name' -f $Name), 27 | 'InvalidSecurityOptionName', 28 | 'InvalidArgument', 29 | $Name 30 | ) 31 | throw $errorRecord 32 | } 33 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityPolicy.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26430.16 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SecurityPolicy", "SecurityPolicy\SecurityPolicy.csproj", "{3A256B08-855C-414F-9E5D-3EE28E7B0FDE}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {3A256B08-855C-414F-9E5D-3EE28E7B0FDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {3A256B08-855C-414F-9E5D-3EE28E7B0FDE}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {3A256B08-855C-414F-9E5D-3EE28E7B0FDE}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {3A256B08-855C-414F-9E5D-3EE28E7B0FDE}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/InitializeModule.ps1: -------------------------------------------------------------------------------- 1 | function InitializeModule { 2 | ImportSecurityOptionData 3 | ImportUserRightData 4 | 5 | $manifest = Join-Path $myinvocation.MyCommand.Module.ModuleBase ('{0}.psd1' -f $myinvocation.MyCommand.Module.Name) 6 | $commands = (Import-PowerShellDataFile $manifest).FunctionsToExport 7 | Register-ArgumentCompleter -CommandName ($commands -like '*UserRight') -ParameterName Name -ScriptBlock { 8 | param ( 9 | [String]$CommandName, 10 | 11 | [String]$ParameterName, 12 | 13 | [String]$WordToComplete 14 | ) 15 | 16 | [Indented.SecurityPolicy.UserRight].GetEnumValues().Where{ 17 | $_ -like "$WordToComplete*" 18 | } 19 | } 20 | 21 | Register-ArgumentCompleter -CommandName ($commands -like '*SecurityOption') -ParameterName Name -ScriptBlock { 22 | param ( 23 | [String]$CommandName, 24 | 25 | [String]$ParameterName, 26 | 27 | [String]$WordToComplete 28 | ) 29 | 30 | (Resolve-SecurityOption | Where-Object Name -like "$WordToComplete*").Name 31 | } 32 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/private/OpenLsaPolicy.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Management.Automation 2 | 3 | function OpenLsaPolicy { 4 | <# 5 | .SYNOPSIS 6 | Open the LSA policy handle. 7 | .DESCRIPTION 8 | Open the LSA policy handle. 9 | #> 10 | 11 | [CmdletBinding()] 12 | [OutputType([Indented.SecurityPolicy.Lsa])] 13 | param ( ) 14 | 15 | try { 16 | return [Indented.SecurityPolicy.Lsa]::new() 17 | } catch { 18 | $innerException = $_.Exception.GetBaseException() 19 | if ($innerException -is [UnauthorizedAccessException]) { 20 | $exception = [UnauthorizedAccessException]::new('Cannot open LSA: Access denied', $innerException) 21 | $category = 'PermissionDenied' 22 | } else { 23 | $exception = [InvalidOperationException]::new('An error occurred when opening the LSA', $innerException) 24 | $category = 'OperationStopped' 25 | } 26 | $errorRecord = [ErrorRecord]::new( 27 | $exception, 28 | 'CannotOpenLSA', 29 | $category, 30 | $null 31 | ) 32 | throw $errorRecord 33 | } 34 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/Dsc/GroupManagedServiceAccount.ps1: -------------------------------------------------------------------------------- 1 | [DscResource()] 2 | class GroupManagedServiceAccount { 3 | [DscProperty()] 4 | [Ensure]$Ensure = 'Present' 5 | 6 | [DscProperty(Key)] 7 | [String]$Name 8 | 9 | [GroupManagedServiceAccount] Get() { 10 | if (Test-GroupManagedServiceAccount -AccountName $this.Name) { 11 | $this.Ensure = 'Present' 12 | } else { 13 | $this.Ensure = 'Absent' 14 | } 15 | 16 | return $this 17 | } 18 | 19 | [Void] Set() { 20 | if ($this.Ensure -eq 'Present' -and -not (Test-GroupManagedServiceAccount -AccountName $this.Name)) { 21 | Install-GroupManagedServiceAccount -AccountName $this.Name 22 | } elseif ($this.Ensure -eq 'Absent' -and (Test-GroupManagedServiceAccount -AccountName $this.Name)) { 23 | Uninstall-GroupManagedServiceAccount -AccountName $this.Name 24 | } 25 | } 26 | 27 | [Boolean] Test() { 28 | if ($this.Ensure -eq 'Present') { 29 | return Test-GroupManagedServiceAccount -AccountName $this.Name 30 | } elseif ($this.Ensure -eq 'Absent') { 31 | return -not (Test-GroupManagedServiceAccount -AccountName $this.Name) 32 | } 33 | return $true 34 | } 35 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/private/ImportUserRightData.ps1: -------------------------------------------------------------------------------- 1 | using namespace Indented.SecurityPolicy 2 | 3 | function ImportUserRightData { 4 | <# 5 | .SYNOPSIS 6 | Import and merge localized user rights data. 7 | .DESCRIPTION 8 | Import and merge localized user rights data. 9 | #> 10 | 11 | [CmdletBinding()] 12 | param ( ) 13 | 14 | try { 15 | $params = @{ 16 | FileName = 'userRights' 17 | BindingVariable = 'localizedUserRights' 18 | BaseDirectory = $myinvocation.MyCommand.Module.ModuleBase 19 | ErrorAction = 'Stop' 20 | } 21 | Import-LocalizedData @params 22 | } catch { 23 | Import-LocalizedData @params -UICulture en 24 | } 25 | 26 | $Script:userRightData = @{} 27 | $Script:userRightLookupHelper = @{} 28 | 29 | foreach ($key in $localizedUserRights.Keys) { 30 | $value = [PSCustomObject]@{ 31 | Name = [UserRight]$key 32 | Description = $localizedUserRights[$key] 33 | PSTypeName = 'Indented.SecurityPolicy.UserRightInfo' 34 | } 35 | $Script:userRightData.Add($key, $value) 36 | $Script:userRightLookupHelper.Add($value.Description, $key) 37 | } 38 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/private/GetIadsLocalUser.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe GetIadsLocalUser { 19 | BeforeAll { 20 | $defaultParams = @{ 21 | Sid = [System.Security.Principal.SecurityIdentifier]::new([Byte[]](@(1) * 12), 0) 22 | } 23 | } 24 | 25 | It 'Gets users from the local account database' { 26 | GetIadsLocalUser | Should -Not -BeNullOrEmpty 27 | } 28 | 29 | It 'Gets a user by name' { 30 | $guest = GetIadsLocalUser | Where-Object Sid -like '*501' 31 | 32 | GetIadsLocalUser -Name $guest.Name | Should -Not -BeNullOrEmpty 33 | } 34 | 35 | It 'Gets a user by SID' { 36 | $guest = GetIadsLocalUser | Where-Object Sid -like '*501' 37 | 38 | GetIadsLocalUser -Sid $guest.Sid | Should -Not -BeNullOrEmpty 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/public/Uninstall-GroupManagedServiceAccount.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Management.Automation 2 | 3 | function Uninstall-GroupManagedServiceAccount { 4 | <# 5 | .SYNOPSIS 6 | Uninstalls a Group Managed Service Account from the local machine. 7 | .DESCRIPTION 8 | Uninstalls a Group Managed Service Account from the local machine. 9 | #> 10 | 11 | [CmdletBinding()] 12 | param ( 13 | # The name of the Group Managed Service Account. 14 | [Parameter(Mandatory, Position = 1, ValueFromPipeline, ValueFromPipelineByPropertyName)] 15 | [String]$AccountName 16 | ) 17 | 18 | process { 19 | try { 20 | if (Test-GroupManagedServiceAccount $AccountName) { 21 | [Indented.SecurityPolicy.ServiceAccount]::RemoveServiceAccount($AccountName) 22 | } else { 23 | $errorRecord = [ErrorRecord]::new( 24 | [ArgumentException]::new('The specified account, {0}, is not installed' -f $AccountName), 25 | 'GMSANotInstalled', 26 | 'InvalidArgument', 27 | $AccountName 28 | ) 29 | $pscmdlet.ThrowTerminatingError($errorRecord) 30 | } 31 | } catch { 32 | $pscmdlet.ThrowTerminatingError($_) 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/public/Clear-UserRight.ps1: -------------------------------------------------------------------------------- 1 | using namespace Indented.SecurityPolicy 2 | 3 | function Clear-UserRight { 4 | <# 5 | .SYNOPSIS 6 | Clears the accounts from the specified user right. 7 | .DESCRIPTION 8 | Clears the accounts from the specified user right. 9 | .EXAMPLE 10 | Clear-UserRight 'Log on as a batch job' 11 | 12 | Clear the SeBatchLogonRight right. 13 | #> 14 | 15 | [CmdletBinding()] 16 | param ( 17 | # The name of the user right to clear. 18 | [Parameter(Mandatory, Position = 1, ValueFromPipeline, ValueFromPipelineByPropertyName)] 19 | [ValidateScript( { $_ | Resolve-UserRight } )] 20 | [Alias('UserRight')] 21 | [String[]]$Name 22 | ) 23 | 24 | begin { 25 | try { 26 | $lsa = OpenLsaPolicy 27 | } catch { 28 | $pscmdlet.ThrowTerminatingError($_) 29 | } 30 | } 31 | 32 | process { 33 | foreach ($userRight in $Name | Resolve-UserRight) { 34 | try { 35 | foreach ($identity in $lsa.EnumerateAccountsWithUserRight($userRight.Name)) { 36 | $lsa.RemoveAccountRights($identity, [UserRight[]]$userRight.Name) 37 | } 38 | } catch { 39 | Write-Error -ErrorRecord $_ 40 | } 41 | } 42 | } 43 | 44 | end { 45 | CloseLsaPolicy $lsa 46 | } 47 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/help/Clear-UserRight.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Indented.SecurityPolicy-help.xml 3 | Module Name: Indented.SecurityPolicy 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Clear-UserRight 9 | 10 | ## SYNOPSIS 11 | Clears the accounts from the specified user right. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Clear-UserRight [-Name] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | Clears the accounts from the specified user right. 21 | 22 | ## EXAMPLES 23 | 24 | ### EXAMPLE 1 25 | ``` 26 | Clear-UserRight 'Log on as a batch job' 27 | ``` 28 | 29 | Clear the SeBatchLogonRight right. 30 | 31 | ## PARAMETERS 32 | 33 | ### -Name 34 | The name of the user right to clear. 35 | 36 | ```yaml 37 | Type: String[] 38 | Parameter Sets: (All) 39 | Aliases: UserRight 40 | 41 | Required: True 42 | Position: 2 43 | Default value: None 44 | Accept pipeline input: True (ByPropertyName, ByValue) 45 | Accept wildcard characters: False 46 | ``` 47 | 48 | ### CommonParameters 49 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 50 | 51 | ## INPUTS 52 | 53 | ## OUTPUTS 54 | 55 | ## NOTES 56 | 57 | ## RELATED LINKS 58 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/help/Install-GroupManagedServiceAccount.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Indented.SecurityPolicy-help.xml 3 | Module Name: Indented.SecurityPolicy 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Install-GroupManagedServiceAccount 9 | 10 | ## SYNOPSIS 11 | Adds a Group Managed Service Account to the local machine. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Install-GroupManagedServiceAccount [-AccountName] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | Adds a Group Managed Service Account to the local machine. 21 | 22 | ## EXAMPLES 23 | 24 | ### EXAMPLE 1 25 | ``` 26 | Install-GroupManagedServiceAccount -AccountName domain\name$ 27 | ``` 28 | 29 | ## PARAMETERS 30 | 31 | ### -AccountName 32 | The name of the Group Managed Service Account. 33 | 34 | ```yaml 35 | Type: String 36 | Parameter Sets: (All) 37 | Aliases: 38 | 39 | Required: True 40 | Position: 2 41 | Default value: None 42 | Accept pipeline input: True (ByPropertyName, ByValue) 43 | Accept wildcard characters: False 44 | ``` 45 | 46 | ### CommonParameters 47 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 48 | 49 | ## INPUTS 50 | 51 | ## OUTPUTS 52 | 53 | ## NOTES 54 | 55 | ## RELATED LINKS 56 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/help/Uninstall-GroupManagedServiceAccount.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Indented.SecurityPolicy-help.xml 3 | Module Name: Indented.SecurityPolicy 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Uninstall-GroupManagedServiceAccount 9 | 10 | ## SYNOPSIS 11 | Uninstalls a Group Managed Service Account from the local machine. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Uninstall-GroupManagedServiceAccount [-AccountName] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | Uninstalls a Group Managed Service Account from the local machine. 21 | 22 | ## EXAMPLES 23 | 24 | ### Example 1 25 | ```powershell 26 | PS C:\> {{ Add example code here }} 27 | ``` 28 | 29 | {{ Add example description here }} 30 | 31 | ## PARAMETERS 32 | 33 | ### -AccountName 34 | The name of the Group Managed Service Account. 35 | 36 | ```yaml 37 | Type: String 38 | Parameter Sets: (All) 39 | Aliases: 40 | 41 | Required: True 42 | Position: 2 43 | Default value: None 44 | Accept pipeline input: True (ByPropertyName, ByValue) 45 | Accept wildcard characters: False 46 | ``` 47 | 48 | ### CommonParameters 49 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 50 | 51 | ## INPUTS 52 | 53 | ## OUTPUTS 54 | 55 | ## NOTES 56 | 57 | ## RELATED LINKS 58 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/public/Resolve-UserRight.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe Resolve-UserRight { 19 | It 'Given a constant name, returns an object with the same name' { 20 | $userRight = Resolve-UserRight SeMachineAccountPrivilege 21 | 22 | $userRight.Name | Should -Be SeMachineAccountPrivilege 23 | $userRight.Description | Should -Be 'Add workstations to domain' 24 | } 25 | 26 | It 'Given a descriptive name, returns an object with the constant name' { 27 | $userRight = Resolve-UserRight 'Add workstations to domain' 28 | 29 | $userRight.Name | Should -Be SeMachineAccountPrivilege 30 | $userRight.Description | Should -Be 'Add workstations to domain' 31 | } 32 | 33 | It 'Given a wildcard, returns all rights with matching descriptions' { 34 | $userRight = Resolve-UserRight "*batch*" 35 | 36 | @($userRight).Count | Should -Be 2 37 | $userRight.Name | Should -Contain 'SeDenyBatchLogonRight' 38 | $userRight.Name | Should -Contain 'SeBatchLogonRight' 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/help/Test-GroupManagedServiceAccount.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Indented.SecurityPolicy-help.xml 3 | Module Name: Indented.SecurityPolicy 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Test-GroupManagedServiceAccount 9 | 10 | ## SYNOPSIS 11 | Test whether or not a Group Managed Service Account is installed in the NetLogon store. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Test-GroupManagedServiceAccount [-AccountName] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | Test whether or not a Group Managed Service Account is installed in the NetLogon store. 21 | 22 | ## EXAMPLES 23 | 24 | ### Example 1 25 | ```powershell 26 | PS C:\> {{ Add example code here }} 27 | ``` 28 | 29 | {{ Add example description here }} 30 | 31 | ## PARAMETERS 32 | 33 | ### -AccountName 34 | The name of the Group Managed Service Account. 35 | 36 | ```yaml 37 | Type: String 38 | Parameter Sets: (All) 39 | Aliases: 40 | 41 | Required: True 42 | Position: 2 43 | Default value: None 44 | Accept pipeline input: True (ByPropertyName, ByValue) 45 | Accept wildcard characters: False 46 | ``` 47 | 48 | ### CommonParameters 49 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 50 | 51 | ## INPUTS 52 | 53 | ## OUTPUTS 54 | 55 | ### System.Boolean 56 | ## NOTES 57 | 58 | ## RELATED LINKS 59 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityPolicy/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SecurityPolicy")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Indented Automation")] 12 | [assembly: AssemblyProduct("SecurityPolicy")] 13 | [assembly: AssemblyCopyright("Copyright © Indented Automation 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("4abe05ff-6648-46a7-9c36-5e7c4a5c7d0b")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2017 2 | 3 | version: 1.3.2.{build} 4 | 5 | branches: 6 | only: 7 | - master 8 | 9 | skip_commits: 10 | message: /updated? readme.*s/ 11 | 12 | environment: 13 | nugetapikey: 14 | secure: kp9PPkiJ/iiPfX0b1m/NYh88GLaok3NlJc1XAr6rWH+umpCiZVwvsK9CVfMNYElL 15 | 16 | build: false 17 | 18 | install: 19 | - ps: | 20 | Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force 21 | Set-PSRepository -Name PSGallery -InstallationPolicy Trusted 22 | Install-Module InvokeBuild, Indented.Build 23 | Set-Location $env:APPVEYOR_BUILD_FOLDER 24 | - pwsh: | 25 | Install-Module InvokeBuild, Indented.Build 26 | Set-Location $env:APPVEYOR_BUILD_FOLDER 27 | 28 | build_script: 29 | - ps: Start-Build -BuildType Setup, Build 30 | 31 | test_script: 32 | - ps: Start-Build -BuildType Setup, Test 33 | - pwsh: Start-Build -BuildType Setup, Test 34 | 35 | on_success: 36 | - ps: | 37 | $buildInfo = Get-BuildInfo 38 | [Version]$tagVersion = (git describe --tags --abbrev=0 2>$null) -replace "^v" 39 | 40 | if ($tagVersion -eq $buildInfo.Version) { 41 | $galleryVersion = [Version](Find-Module $buildInfo.ModuleName).Version 42 | if ($buildInfo.Version -gt $galleryVersion) { 43 | Start-Build -BuildType Setup, Publish 44 | } else { 45 | Write-Host "Skipping publish: Already published" -ForegroundColor Green 46 | } 47 | } else { 48 | Write-Host "Skipping publish: Last tag does not match build version" -ForegroundColor Yellow 49 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/public/Get-AssignedUserRight.ps1: -------------------------------------------------------------------------------- 1 | function Get-AssignedUserRight { 2 | <# 3 | .SYNOPSIS 4 | Gets all user rights granted to a principal. 5 | .DESCRIPTION 6 | Get a list of all the user rights granted to one or more principals. Does not expand group membership. 7 | .EXAMPLE 8 | Get-AssignedUserRight 9 | 10 | Returns a list of all defined for the current user. 11 | .EXAMPLE 12 | Get-AssignedUserRight Administrator 13 | 14 | Get the list of rights assigned to the administrator account. 15 | #> 16 | 17 | [CmdletBinding()] 18 | [OutputType('Indented.SecurityPolicy.AssignedUserRight')] 19 | param ( 20 | # Find rights for the specified account names. By default AccountName is the current user. 21 | [Parameter(Position = 1, ValueFromPipelineByPropertyName, ValueFromPipeline)] 22 | [String[]]$AccountName = $env:USERNAME 23 | ) 24 | 25 | begin { 26 | try { 27 | $lsa = OpenLsaPolicy 28 | } catch { 29 | $pscmdlet.ThrowTerminatingError($_) 30 | } 31 | } 32 | 33 | process { 34 | foreach ($account in $AccountName) { 35 | try { 36 | [PSCustomObject]@{ 37 | AccountName = $account 38 | Name = $lsa.EnumerateAccountRights($account) 39 | PSTypeName = 'Indented.SecurityPolicy.AssignedUserRight' 40 | } 41 | } catch { 42 | Write-Error -ErrorRecord $_ 43 | } 44 | } 45 | } 46 | 47 | end { 48 | CloseLsaPolicy $lsa 49 | } 50 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/private/GetIadsLocalUser.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Security.Principal 2 | 3 | function GetIadsLocalUser { 4 | <# 5 | .SYNOPSIS 6 | ADSI based alternative to Get-LocalUser. 7 | .DESCRIPTION 8 | This simple ADSI-based version of Get-LocalUser allows use the AccountStatus and RenameAccount security options on PowerShell Core. 9 | #> 10 | 11 | [CmdletBinding()] 12 | param ( 13 | # The exact name of a user. 14 | [String]$Name, 15 | 16 | # The SID of a user. 17 | [SecurityIdentifier]$Sid, 18 | 19 | # Return the IAdsUser object as-is. 20 | [Switch]$AsIadsUser 21 | ) 22 | 23 | $iadsComputer = [ADSI]('WinNT://{0}' -f $env:COMPUTERNAME) 24 | $null = $iadsComputer.Children.SchemaFilter.Add('user') 25 | 26 | if ($Name) { 27 | $iadsUserCollection = $iadsComputer.Children.Find( 28 | $Name, 29 | 'user' 30 | ) 31 | } else { 32 | $iadsUserCollection = $iadsComputer.Children 33 | } 34 | 35 | foreach ($iadsUser in $iadsUserCollection) { 36 | $iadsUserSid = [SecurityIdentifier]::new([Byte[]]$iadsUser.Get('objectSID'), 0) 37 | 38 | if (-not $Sid -or $iadsUserSid -eq $Sid) { 39 | if ($AsIadsUser) { 40 | $iadsUser 41 | } else { 42 | [PSCustomObject]@{ 43 | Name = $iadsUser.Get('Name') 44 | Enabled = -not ($iadsUser.Get('userFlags') -band 2) 45 | SID = $iadsUserSid 46 | } 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/help/Get-SecurityOption.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Indented.SecurityPolicy-help.xml 3 | Module Name: Indented.SecurityPolicy 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Get-SecurityOption 9 | 10 | ## SYNOPSIS 11 | Get the value of a security option. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Get-SecurityOption [[-Name] ] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | Get the value of a security option. 21 | 22 | ## EXAMPLES 23 | 24 | ### EXAMPLE 1 25 | ``` 26 | Get-SecurityOption 'Accounts: Administrator account status' 27 | ``` 28 | 29 | Gets the current value of the administrator account status policy (determined by the state of the account). 30 | 31 | ### EXAMPLE 2 32 | ``` 33 | Get-SecurityOption 'EnableLUA' 34 | ``` 35 | 36 | Get the value of the "User Account Control: Run all administrators in Admin Approval Mode" policy. 37 | 38 | ## PARAMETERS 39 | 40 | ### -Name 41 | The name of the security option to set. 42 | 43 | ```yaml 44 | Type: String[] 45 | Parameter Sets: (All) 46 | Aliases: 47 | 48 | Required: False 49 | Position: 2 50 | Default value: None 51 | Accept pipeline input: True (ByPropertyName, ByValue) 52 | Accept wildcard characters: False 53 | ``` 54 | 55 | ### CommonParameters 56 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 57 | 58 | ## INPUTS 59 | 60 | ## OUTPUTS 61 | 62 | ## NOTES 63 | 64 | ## RELATED LINKS 65 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityPolicy/Account.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Runtime.InteropServices; 4 | using System.Security.Principal; 5 | using System.Text; 6 | 7 | namespace Indented.SecurityPolicy 8 | { 9 | public static class Account 10 | { 11 | public static SecurityIdentifier LookupAccountName(string SystemName, string AccountName) 12 | { 13 | const int ERROR_INSUFFICIENT_BUFFER = 122; 14 | 15 | uint cbSid = 0; 16 | uint cchReferencedDomainName = 0; 17 | SID_NAME_USE peUse; 18 | 19 | UnsafeNativeMethods.LookupAccountName( 20 | SystemName, 21 | AccountName, 22 | null, 23 | ref cbSid, 24 | null, 25 | ref cchReferencedDomainName, 26 | out peUse 27 | ); 28 | 29 | if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER) 30 | { 31 | byte[] Sid = new byte[cbSid]; 32 | StringBuilder ReferencedDomainName = new StringBuilder((int)cchReferencedDomainName); 33 | 34 | if (UnsafeNativeMethods.LookupAccountName( 35 | SystemName, 36 | AccountName, 37 | Sid, 38 | ref cbSid, 39 | ReferencedDomainName, 40 | ref cchReferencedDomainName, 41 | out peUse 42 | )) { 43 | 44 | return new SecurityIdentifier(Sid, 0); 45 | } 46 | } 47 | 48 | throw new Win32Exception(Marshal.GetLastWin32Error()); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/help/Get-AssignedUserRight.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Indented.SecurityPolicy-help.xml 3 | Module Name: Indented.SecurityPolicy 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Get-AssignedUserRight 9 | 10 | ## SYNOPSIS 11 | Gets all user rights granted to a principal. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Get-AssignedUserRight [[-AccountName] ] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | Get a list of all the user rights granted to one or more principals. 21 | Does not expand group membership. 22 | 23 | ## EXAMPLES 24 | 25 | ### EXAMPLE 1 26 | ``` 27 | Get-AssignedUserRight 28 | ``` 29 | 30 | Returns a list of all defined for the current user. 31 | 32 | ### EXAMPLE 2 33 | ``` 34 | Get-AssignedUserRight Administrator 35 | ``` 36 | 37 | Get the list of rights assigned to the administrator account. 38 | 39 | ## PARAMETERS 40 | 41 | ### -AccountName 42 | Find rights for the specified account names. 43 | By default AccountName is the current user. 44 | 45 | ```yaml 46 | Type: String[] 47 | Parameter Sets: (All) 48 | Aliases: 49 | 50 | Required: False 51 | Position: 2 52 | Default value: $env:USERNAME 53 | Accept pipeline input: True (ByPropertyName, ByValue) 54 | Accept wildcard characters: False 55 | ``` 56 | 57 | ### CommonParameters 58 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 59 | 60 | ## INPUTS 61 | 62 | ## OUTPUTS 63 | 64 | ### Indented.SecurityPolicy.AssignedUserRight 65 | ## NOTES 66 | 67 | ## RELATED LINKS 68 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/public/Grant-UserRight.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Security.Principal 2 | 3 | function Grant-UserRight { 4 | <# 5 | .SYNOPSIS 6 | Grant rights to an account. 7 | .DESCRIPTION 8 | Grants one or more rights to the specified accounts. 9 | .EXAMPLE 10 | Grant-UserRight -Name 'Allow logon locally' -AccountName 'Administrators' 11 | 12 | Grants the allow logon locally right to the Administrators group. 13 | #> 14 | 15 | [CmdletBinding(SupportsShouldProcess)] 16 | param ( 17 | # The user right, or rights, to query. 18 | [Parameter(Mandatory, Position = 1, ValueFromPipelineByPropertyName)] 19 | [ValidateScript( { $_ | Resolve-UserRight } )] 20 | [Alias('UserRight')] 21 | [String[]]$Name, 22 | 23 | # Grant rights to the specified accounts. Each account may be a string, an NTAccount object, or a SecurityIdentifier object. 24 | [Parameter(Mandatory, Position = 2, ValueFromPipeline, ValueFromPipelineByPropertyName)] 25 | [Object[]]$AccountName 26 | ) 27 | 28 | begin { 29 | try { 30 | $lsa = OpenLsaPolicy 31 | } catch { 32 | $pscmdlet.ThrowTerminatingError($_) 33 | } 34 | } 35 | 36 | process { 37 | foreach ($account in $AccountName) { 38 | try { 39 | $userRights = $Name | Resolve-UserRight 40 | 41 | if ($pscmdlet.ShouldProcess(('Adding {0} to {1}' -f $account, $userRights.Name -join ', '))) { 42 | $lsa.AddAccountRights($account, $userRights.Name) 43 | } 44 | } catch { 45 | Write-Error -ErrorRecord $_ 46 | } 47 | } 48 | } 49 | 50 | end { 51 | CloseLsaPolicy $lsa 52 | } 53 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/help/Get-UserRight.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Indented.SecurityPolicy-help.xml 3 | Module Name: Indented.SecurityPolicy 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Get-UserRight 9 | 10 | ## SYNOPSIS 11 | Gets all accounts that are assigned a specified user right. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Get-UserRight [[-Name] ] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | Gets a list of all accounts that hold a specified user right. 21 | 22 | Group membership is not evaluated, the values returned are explicitly listed under the specified user rights. 23 | 24 | ## EXAMPLES 25 | 26 | ### EXAMPLE 1 27 | ``` 28 | Get-UserRight SeServiceLogonRight 29 | ``` 30 | 31 | Returns a list of all accounts that hold the "Log on as a service" right. 32 | 33 | ### EXAMPLE 2 34 | ``` 35 | Get-UserRight SeServiceLogonRight, SeDebugPrivilege 36 | ``` 37 | 38 | Returns accounts with the SeServiceLogonRight and SeDebugPrivilege rights. 39 | 40 | ## PARAMETERS 41 | 42 | ### -Name 43 | The user right, or rights, to query. 44 | 45 | ```yaml 46 | Type: String[] 47 | Parameter Sets: (All) 48 | Aliases: UserRight 49 | 50 | Required: False 51 | Position: 2 52 | Default value: None 53 | Accept pipeline input: True (ByPropertyName, ByValue) 54 | Accept wildcard characters: False 55 | ``` 56 | 57 | ### CommonParameters 58 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 59 | 60 | ## INPUTS 61 | 62 | ## OUTPUTS 63 | 64 | ### Indented.SecurityPolicy.UserRight 65 | ## NOTES 66 | 67 | ## RELATED LINKS 68 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/public/Reset-SecurityOption.ps1: -------------------------------------------------------------------------------- 1 | function Reset-SecurityOption { 2 | <# 3 | .SYNOPSIS 4 | Reset the value of a security option to its default. 5 | .DESCRIPTION 6 | Reset the value of a security option to its default. 7 | .EXAMPLE 8 | Reset-SecurityOption FIPSAlgorithmPolicy 9 | 10 | Resets the FIPSAlgorithmPolicy policy to the default value, Disabled. 11 | .EXAMPLE 12 | Reset-SecurityOption 'Interactive logon: Message text for users attempting to log on' 13 | 14 | Resets the LegalNoticeText policy to an empty string. 15 | .EXAMPLE 16 | Reset-SecurityOption ForceKeyProtection 17 | 18 | Resets the ForceKeyProtection policy to "Not Defined" by removing the associated registry value. 19 | #> 20 | 21 | [CmdletBinding(SupportsShouldProcess)] 22 | param ( 23 | # The name of the security option to set. 24 | [Parameter(Mandatory, Position = 1, ValueFromPipeline, ValueFromPipelineByPropertyName)] 25 | [String[]]$Name 26 | ) 27 | 28 | process { 29 | foreach ($securityOptionInfo in $Name | Resolve-SecurityOption | Sort-Object Category, ShortDescription) { 30 | $value = $securityOptionInfo.Default 31 | 32 | if ($value -eq 'Not Defined' -and $securityOptionInfo.Key) { 33 | if (Test-Path $securityOptionInfo.Key) { 34 | $key = Get-Item -Path $securityOptionInfo.Key 35 | if ($key.GetValueNames() -contains $securityOptionInfo.Name) { 36 | Remove-ItemProperty -Path $key.PSPath -Name $securityOptionInfo.Name 37 | } 38 | } 39 | } else { 40 | Set-SecurityOption -Name $securityOptionInfo.Name -Value $value 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/private/ImportSecurityOptionData.ps1: -------------------------------------------------------------------------------- 1 | function ImportSecurityOptionData { 2 | <# 3 | .SYNOPSIS 4 | Import and merge localized security option data. 5 | .DESCRIPTION 6 | Import and merge localized security option data. 7 | #> 8 | 9 | [CmdletBinding()] 10 | param ( ) 11 | 12 | try { 13 | $params = @{ 14 | FileName = 'securityOptions' 15 | BindingVariable = 'localizedSecurityOptions' 16 | BaseDirectory = $myinvocation.MyCommand.Module.ModuleBase 17 | ErrorAction = 'Stop' 18 | } 19 | Import-LocalizedData @params 20 | } catch { 21 | Import-LocalizedData @params -UICulture en 22 | } 23 | 24 | $path = Join-Path $myinvocation.MyCommand.Module.ModuleBase 'data\securityOptions.psd1' 25 | $Script:securityOptionData = Import-PowerShellDataFile $path 26 | 27 | # Create the lookup helper 28 | $Script:securityOptionLookupHelper = @{} 29 | # Merge localized descriptions and fill the helper 30 | foreach ($key in [String[]]$Script:securityOptionData.Keys) { 31 | $value = $Script:securityOptionData[$key] 32 | 33 | $description = $localizedSecurityOptions[$key] 34 | $category, $shortDescription = $description -split ': *', 2 35 | 36 | $value.Add('Category', $category) 37 | $value.Add('Description', $description) 38 | $value.Add('ShortDescription', $shortDescription) 39 | $value.Add('PSTypeName', 'Indented.SecurityPolicy.SecurityOptionInfo') 40 | 41 | if (-not $value.Contains('Name')) { 42 | $value.Add('Name', $key) 43 | } 44 | 45 | $value = [PSCustomObject]$value 46 | 47 | $Script:securityOptionData[$key] = $value 48 | $Script:securityOptionLookupHelper.Add($description, $key) 49 | } 50 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/help/Resolve-UserRight.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Indented.SecurityPolicy-help.xml 3 | Module Name: Indented.SecurityPolicy 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Resolve-UserRight 9 | 10 | ## SYNOPSIS 11 | Resolves the name of a user right as shown in the local security policy editor to its constant name. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Resolve-UserRight [[-Name] ] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | Resolves the name of a user right as shown in the local security policy editor to its constant name. 21 | 22 | ## EXAMPLES 23 | 24 | ### EXAMPLE 1 25 | ``` 26 | Resolve-UserRight "Generate security audits" 27 | ``` 28 | 29 | Returns the value SeAuditPrivilege. 30 | 31 | ### EXAMPLE 2 32 | ``` 33 | Resolve-UserRight "*batch*" 34 | ``` 35 | 36 | Returns SeBatchLogonRight and SeDenyBatchLogonRight via the description. 37 | 38 | ### EXAMPLE 3 39 | ``` 40 | Resolve-UserRight SeBatchLogonRight 41 | ``` 42 | 43 | Returns the name and description of the user right. 44 | 45 | ## PARAMETERS 46 | 47 | ### -Name 48 | The name or description of a user right. 49 | Wildcards are supported for description values. 50 | 51 | ```yaml 52 | Type: String 53 | Parameter Sets: (All) 54 | Aliases: 55 | 56 | Required: False 57 | Position: 2 58 | Default value: None 59 | Accept pipeline input: True (ByValue) 60 | Accept wildcard characters: False 61 | ``` 62 | 63 | ### CommonParameters 64 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 65 | 66 | ## INPUTS 67 | 68 | ## OUTPUTS 69 | 70 | ## NOTES 71 | 72 | ## RELATED LINKS 73 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/securityoptions/RenameAccount.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe RenameAccount { 19 | BeforeAll { 20 | Mock GetIadsLocalUser { 21 | [PSCustomObject]@{ 22 | Name = 'Guest' 23 | } 24 | } 25 | Mock RenameIadsLocalUser 26 | } 27 | 28 | BeforeEach { 29 | $class = [RenameAccount]@{ 30 | SidType = 'AccountGuestSid' 31 | Value = 'Guest' 32 | } 33 | } 34 | 35 | Context 'Get' { 36 | It 'Gets the current status of the account' { 37 | $class.Value = 'OtherName' 38 | $instance = $class.Get() 39 | $instance.Value | Should -Be 'Guest' 40 | } 41 | } 42 | 43 | Context 'Set' { 44 | It 'Calls RenameIadsLocalUser' { 45 | $class.Set() 46 | 47 | Assert-MockCalled RenameIadsLocalUser -Times 1 -Scope It 48 | } 49 | } 50 | 51 | Context 'Test' { 52 | It 'When the account name matches, returns true' { 53 | $class.Test() | Should -Be $true 54 | } 55 | 56 | It 'When the account name does not match, returns false' { 57 | $class.Value = 'NewName' 58 | 59 | $class.Test() | Should -Be $false 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityPolicy/Win32SecurityIdentifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Security; 4 | using System.Security.Principal; 5 | using Microsoft.Win32.SafeHandles; 6 | 7 | namespace Indented.SecurityPolicy 8 | { 9 | public class Win32SecurityIdentifier : IDisposable 10 | { 11 | private bool disposed = false; 12 | private GCHandle handle; 13 | private Byte[] buffer; 14 | 15 | public SecurityIdentifier securityIdentifier; 16 | 17 | public Win32SecurityIdentifier(String principal) : this(new NTAccount(principal)) { } 18 | 19 | public Win32SecurityIdentifier(IdentityReference identityReference) : this((SecurityIdentifier)identityReference.Translate(typeof(SecurityIdentifier))) { } 20 | 21 | public Win32SecurityIdentifier(SecurityIdentifier securityIdentifier) 22 | { 23 | this.securityIdentifier = securityIdentifier; 24 | 25 | buffer = new Byte[securityIdentifier.BinaryLength]; 26 | securityIdentifier.GetBinaryForm(buffer, 0); 27 | 28 | handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); 29 | } 30 | 31 | public IntPtr address 32 | { 33 | get { 34 | if (handle.IsAllocated) 35 | { 36 | return handle.AddrOfPinnedObject(); 37 | } 38 | else 39 | { 40 | return IntPtr.Zero; 41 | } 42 | } 43 | } 44 | 45 | public void Dispose() 46 | { 47 | Dispose(true); 48 | GC.SuppressFinalize(this); 49 | } 50 | 51 | protected virtual void Dispose(bool disposing) 52 | { 53 | if (disposed) return; 54 | 55 | if (disposing && handle.IsAllocated) 56 | handle.Free(); 57 | 58 | disposed = true; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/help/Resolve-SecurityOption.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Indented.SecurityPolicy-help.xml 3 | Module Name: Indented.SecurityPolicy 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Resolve-SecurityOption 9 | 10 | ## SYNOPSIS 11 | Resolves the name of a security option as shown in the local security policy editor. 12 | 13 | ## SYNTAX 14 | 15 | ### ByName 16 | ``` 17 | Resolve-SecurityOption [[-Name] ] [] 18 | ``` 19 | 20 | ### ByCategory 21 | ``` 22 | Resolve-SecurityOption -Category [] 23 | ``` 24 | 25 | ## DESCRIPTION 26 | Resolves the name of a security option as shown in the local security policy editor to either the registry value name, or the name of an implementing class. 27 | 28 | ## EXAMPLES 29 | 30 | ### EXAMPLE 1 31 | ``` 32 | Resolve-SecurityOption "User Account Control: Run all administrators in Admin Approval Mode" 33 | ``` 34 | 35 | Returns information about the security option. 36 | 37 | ## PARAMETERS 38 | 39 | ### -Name 40 | The name or description of a user right. 41 | Wildcards are supported for description values. 42 | 43 | ```yaml 44 | Type: String 45 | Parameter Sets: ByName 46 | Aliases: 47 | 48 | Required: False 49 | Position: 2 50 | Default value: None 51 | Accept pipeline input: True (ByValue) 52 | Accept wildcard characters: False 53 | ``` 54 | 55 | ### -Category 56 | Get the policies under a specific category, for example "Network security". 57 | 58 | ```yaml 59 | Type: String 60 | Parameter Sets: ByCategory 61 | Aliases: 62 | 63 | Required: True 64 | Position: Named 65 | Default value: None 66 | Accept pipeline input: False 67 | Accept wildcard characters: False 68 | ``` 69 | 70 | ### CommonParameters 71 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 72 | 73 | ## INPUTS 74 | 75 | ## OUTPUTS 76 | 77 | ## NOTES 78 | 79 | ## RELATED LINKS 80 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/private/RenameIadsLocalUser.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe RenameIadsLocalUser { 19 | BeforeAll { 20 | Mock GetIadsLocalUser { 21 | [PSCustomObject]@{ 22 | DistinguishedName = '' 23 | Path = 'WinNT://DOMAIN/COMPUTER/Name' 24 | } | 25 | Add-Member Get -MemberType ScriptMethod -PassThru -Value { 26 | $Script:currentName 27 | } | 28 | Add-Member Rename -MemberType ScriptMethod -PassThru -Value { 29 | $Script:renameCalled = $true 30 | } 31 | } 32 | 33 | $defaultParams = @{ 34 | Sid = [System.Security.Principal.SecurityIdentifier]::new([Byte[]](@(1) * 12), 0) 35 | NewName = 'notguest' 36 | } 37 | } 38 | 39 | BeforeEach { 40 | $Script:currentName = 'guest' 41 | $Script:renameCalled = $false 42 | } 43 | 44 | It 'Calls GetIadsLocalUser to find the account' { 45 | RenameIadsLocalUser @defaultParams 46 | 47 | Assert-MockCalled GetIadsLocalUser -Scope It 48 | } 49 | 50 | It 'When the account has a different name, calls Rename' { 51 | RenameIadsLocalUser @defaultParams 52 | 53 | $Script:renameCalled | Should -BeTrue 54 | } 55 | 56 | It 'When the account is already named after NewName, does nothing' { 57 | $Script:currentName = 'notguest' 58 | 59 | RenameIadsLocalUser @defaultParams 60 | 61 | $Script:renameCalled | Should -BeFalse 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/public/Get-UserRight.ps1: -------------------------------------------------------------------------------- 1 | function Get-UserRight { 2 | <# 3 | .SYNOPSIS 4 | Gets all accounts that are assigned a specified user right. 5 | .DESCRIPTION 6 | Gets a list of all accounts that hold a specified user right. 7 | 8 | Group membership is not evaluated, the values returned are explicitly listed under the specified user rights. 9 | .EXAMPLE 10 | Get-UserRight SeServiceLogonRight 11 | 12 | Returns a list of all accounts that hold the "Log on as a service" right. 13 | .EXAMPLE 14 | Get-UserRight SeServiceLogonRight, SeDebugPrivilege 15 | 16 | Returns accounts with the SeServiceLogonRight and SeDebugPrivilege rights. 17 | #> 18 | 19 | [CmdletBinding()] 20 | [OutputType('Indented.SecurityPolicy.UserRight')] 21 | param ( 22 | # The user right, or rights, to query. 23 | [Parameter(Position = 1, ValueFromPipelineByPropertyName, ValueFromPipeline)] 24 | [ValidateScript( { $_ | Resolve-UserRight } )] 25 | [Alias('UserRight')] 26 | [String[]]$Name 27 | ) 28 | 29 | begin { 30 | try { 31 | $lsa = OpenLsaPolicy 32 | } catch { 33 | $pscmdlet.ThrowTerminatingError($_) 34 | } 35 | } 36 | 37 | process { 38 | foreach ($userRight in $Name | Resolve-UserRight) { 39 | try { 40 | [PSCustomObject]@{ 41 | Name = $userRight.Name 42 | Description = $userRight.Description 43 | AccountName = $lsa.EnumerateAccountsWithUserRight($userRight.Name) 44 | PSTypeName = 'Indented.SecurityPolicy.UserRightSetting' 45 | } 46 | } catch { 47 | $innerException = $_.Exception.GetBaseException() 48 | $errorRecord = [ErrorRecord]::new( 49 | [InvalidOperationException]::new( 50 | ('An error occurred retrieving the user right {0}: {1}' -f $userRight.Name, $innerException.Message), 51 | $innerException 52 | ), 53 | 'FailedToRetrieveUserRight', 54 | 'OperationStopped', 55 | $null 56 | ) 57 | Write-Error -ErrorRecord $errorRecord 58 | } 59 | } 60 | } 61 | 62 | end { 63 | CloseLsaPolicy $lsa 64 | } 65 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/public/Resolve-UserRight.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Management.Automation 2 | using namespace Indented.SecurityPolicy 3 | 4 | function Resolve-UserRight { 5 | <# 6 | .SYNOPSIS 7 | Resolves the name of a user right as shown in the local security policy editor to its constant name. 8 | .DESCRIPTION 9 | Resolves the name of a user right as shown in the local security policy editor to its constant name. 10 | .EXAMPLE 11 | Resolve-UserRight "Generate security audits" 12 | 13 | Returns the value SeAuditPrivilege. 14 | .EXAMPLE 15 | Resolve-UserRight "*batch*" 16 | 17 | Returns SeBatchLogonRight and SeDenyBatchLogonRight via the description. 18 | .EXAMPLE 19 | Resolve-UserRight SeBatchLogonRight 20 | 21 | Returns the name and description of the user right. 22 | #> 23 | 24 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] 25 | [CmdletBinding()] 26 | param ( 27 | # The name or description of a user right. Wildcards are supported for description values. 28 | [Parameter(Position = 1, ValueFromPipeline)] 29 | [String]$Name 30 | ) 31 | 32 | process { 33 | if ($Name) { 34 | if ($Script:userRightData.Contains($Name)) { 35 | $Script:userRightData[$Name] 36 | } elseif ($Script:userRightLookupHelper.Contains($Name)) { 37 | $Script:userRightData[$Script:userRightLookupHelper[$Name]] 38 | } else { 39 | $isLikeDescription = $false 40 | foreach ($value in $Script:userRightLookupHelper.Keys -like $Name) { 41 | $isLikeDescription = $true 42 | $Script:userRightData[$Script:userRightLookupHelper[$value]] 43 | } 44 | if (-not $isLikeDescription) { 45 | $errorRecord = [ErrorRecord]::new( 46 | [ArgumentException]::new('"{0}" does not resolve to a user right' -f $Name), 47 | 'UserRightCannotResolve', 48 | 'InvalidArgument', 49 | $Name 50 | ) 51 | $pscmdlet.ThrowTerminatingError($errorRecord) 52 | } 53 | } 54 | } else { 55 | $Script:userRightData.Values 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/private/EnableIadsLocalUser.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe EnableIadsLocalUser { 19 | BeforeAll { 20 | Mock GetIadsLocalUser { 21 | [PSCustomObject]@{ 22 | DistinguishedName = '' 23 | Path = 'WinNT://DOMAIN/COMPUTER/Name' 24 | } | 25 | Add-Member Get -MemberType ScriptMethod -PassThru -Value { 26 | $Script:userFlagsValue 27 | } | 28 | Add-Member Put -MemberType ScriptMethod -PassThru -Value { 29 | $Script:putValue = $args[1] 30 | } | 31 | Add-Member SetInfo -MemberType ScriptMethod -PassThru -Value { 32 | $Script:setInfoCalled = $true 33 | } 34 | } 35 | 36 | $defaultParams = @{ 37 | Sid = [System.Security.Principal.SecurityIdentifier]::new([Byte[]](@(1) * 12), 0) 38 | } 39 | } 40 | 41 | BeforeEach { 42 | $Script:userFlagsValue = 2 43 | $Script:putValue = -1 44 | $Script:setInfoCalled = $false 45 | } 46 | 47 | It 'Calls GetIadsLocalUser to find the account' { 48 | EnableIadsLocalUser @defaultParams 49 | 50 | Assert-MockCalled GetIadsLocalUser -Scope It 51 | } 52 | 53 | It 'When the account is disabled, sets userFlags and calls SetInfo' { 54 | EnableIadsLocalUser @defaultParams 55 | 56 | $Script:putValue | Should -Be 0 57 | $Script:setInfoCalled | Should -BeTrue 58 | } 59 | 60 | It 'When the account is enabled, does nothing' { 61 | $Script:userFlagsValue = 0 62 | 63 | EnableIadsLocalUser @defaultParams 64 | 65 | $Script:putValue | Should -Be -1 66 | $Script:setInfoCalled | Should -BeFalse 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/private/DisableIadsLocalUser.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe DisableIadsLocalUser { 19 | BeforeAll { 20 | Mock GetIadsLocalUser { 21 | [PSCustomObject]@{ 22 | DistinguishedName = '' 23 | Path = 'WinNT://DOMAIN/COMPUTER/Name' 24 | } | 25 | Add-Member Get -MemberType ScriptMethod -PassThru -Value { 26 | $Script:userFlagsValue 27 | } | 28 | Add-Member Put -MemberType ScriptMethod -PassThru -Value { 29 | $Script:putValue = $args[1] 30 | } | 31 | Add-Member SetInfo -MemberType ScriptMethod -PassThru -Value { 32 | $Script:setInfoCalled = $true 33 | } 34 | } 35 | 36 | $defaultParams = @{ 37 | Sid = [System.Security.Principal.SecurityIdentifier]::new([Byte[]](@(1) * 12), 0) 38 | } 39 | } 40 | 41 | BeforeEach { 42 | $Script:userFlagsValue = 0 43 | $Script:putValue = -1 44 | $Script:setInfoCalled = $false 45 | } 46 | 47 | It 'Calls GetIadsLocalUser to find the account' { 48 | DisableIadsLocalUser @defaultParams 49 | 50 | Assert-MockCalled GetIadsLocalUser -Scope It 51 | } 52 | 53 | It 'When the account is enabled, sets userFlags and calls SetInfo' { 54 | DisableIadsLocalUser @defaultParams 55 | 56 | $Script:putValue | Should -Be 2 57 | $Script:setInfoCalled | Should -BeTrue 58 | } 59 | 60 | It 'When the account is disabled, does nothing' { 61 | $Script:userFlagsValue = 2 62 | 63 | DisableIadsLocalUser @defaultParams 64 | 65 | $Script:putValue | Should -Be -1 66 | $Script:setInfoCalled | Should -BeFalse 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/public/Revoke-UserRight.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Security.Principal 2 | 3 | function Revoke-UserRight { 4 | <# 5 | .SYNOPSIS 6 | Revokes rights granted to an account. 7 | .DESCRIPTION 8 | Revokes the rights granted to an account or set of accounts. 9 | 10 | The All switch may be used to revoke all rights granted to the specified accounts. 11 | .EXAMPLE 12 | Revoke-UserRight -Name 'Log on as a service' -AccountName 'JonDoe' 13 | 14 | Revokes the logon as a service right granted to the account named JonDoe. 15 | .EXAMPLE 16 | Revoke-UserRight -AccountName 'JonDoe' -All 17 | 18 | Revokes all rights which have been granted to the identity JonDoe. 19 | #> 20 | 21 | [CmdletBinding(DefaultParameterSetName = 'List', SupportsShouldProcess)] 22 | param ( 23 | # The user right, or rights, to query. 24 | [Parameter(Mandatory, Position = 1, ParameterSetName = 'List')] 25 | [ValidateScript( { $_ | Resolve-UserRight } )] 26 | [Alias('UserRight')] 27 | [String[]]$Name, 28 | 29 | # Grant rights to the specified principals. The principal may be a string, an NTAccount object, or a SecurityIdentifier object. 30 | [Parameter(Mandatory, Position = 2)] 31 | [Object[]]$AccountName, 32 | 33 | # Clear all rights granted to the specified accounts. 34 | [Parameter(Mandatory, ParameterSetName = 'All')] 35 | [Switch]$AllRights 36 | ) 37 | 38 | begin { 39 | try { 40 | $lsa = OpenLsaPolicy 41 | } catch { 42 | $pscmdlet.ThrowTerminatingError($_) 43 | } 44 | } 45 | 46 | process { 47 | foreach ($account in $AccountName) { 48 | try { 49 | if ($pscmdlet.ParameterSetName -eq 'All' -and $AllRights) { 50 | if ($pscmdlet.ShouldProcess(('Removing all rights from {0}' -f $account))) { 51 | $lsa.RemoveAllAccountRights($account) 52 | } 53 | } elseif ($pscmdlet.ParameterSetName -eq 'List') { 54 | $userRights = $Name | Resolve-UserRight 55 | 56 | if ($pscmdlet.ShouldProcess(('Removing {0} from {1}' -f $account, $userRights.Name -join ', '))) { 57 | $lsa.RemoveAccountRights($account, $userRights.Name) 58 | } 59 | } 60 | } catch { 61 | Write-Error -ErrorRecord $_ 62 | } 63 | } 64 | } 65 | 66 | end { 67 | CloseLsaPolicy $lsa 68 | } 69 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/public/Get-AssignedUserRight.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe Get-AssignedUserRight { 19 | BeforeAll { 20 | Mock CloseLsaPolicy 21 | Mock OpenLsaPolicy { 22 | [PSCustomObject]@{} | 23 | Add-Member EnumerateAccountRights -MemberType ScriptMethod -PassThru -Value { 24 | 'SeDenyServiceLogonRight' 25 | } 26 | } 27 | 28 | $defaultParams = @{ 29 | AccountName = 'username' 30 | } 31 | } 32 | 33 | Context 'Account has rights assigned' { 34 | It 'When a account has rights assigned, returns the rights' { 35 | $result = Get-AssignedUserRight @defaultParams 36 | 37 | Assert-MockCalled OpenLsaPolicy -Scope It -Times 1 38 | Assert-MockCalled CloseLsaPolicy -Scope It -Times 1 39 | 40 | $result | Should -Not -BeNullOrEmpty 41 | @($result).Count | Should -Be 1 42 | $result.AccountName | Should -Be 'username' 43 | $result.Name | Should -Be 'SeDenyServiceLogonRight' 44 | } 45 | } 46 | 47 | Context 'Error handling - EnumerateAccountsWithUserRight' { 48 | BeforeAll { 49 | Mock OpenLsaPolicy { 50 | [PSCustomObject]@{} | 51 | Add-Member EnumerateAccountRights -MemberType ScriptMethod -PassThru -Value { 52 | throw 'enumerate error' 53 | } 54 | } 55 | } 56 | 57 | It 'When EnumerateAccountRights throws, writes an error' { 58 | { Get-AssignedUserRight @defaultParams -ErrorAction Stop } | Should -Throw 'enumerate error' 59 | { Get-AssignedUserRight @defaultParams -ErrorAction SilentlyContinue } | Should -Not -Throw 60 | 61 | Assert-MockCalled OpenLsaPolicy -Scope It -Times 2 62 | Assert-MockCalled CloseLsaPolicy -Scope It -Times 1 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/Dsc/SecurityOption.ps1: -------------------------------------------------------------------------------- 1 | [DscResource()] 2 | class SecurityOption { 3 | [DscProperty()] 4 | [Ensure]$Ensure = 'Present' 5 | 6 | [DscProperty(Key)] 7 | [String]$Name 8 | 9 | [DscProperty()] 10 | [String[]]$Value 11 | 12 | [DscProperty(NotConfigurable)] 13 | [String]$Description 14 | 15 | [Object]$ParsedValue 16 | 17 | Hidden [Void] ParseValue() { 18 | $securityOptionInfo = Resolve-SecurityOption $this.Name 19 | $valueType = $securityOptionInfo.ValueType -as [Type] 20 | 21 | $candidateValue = $this.Value 22 | if ($valueType.BaseType -ne [Array]) { 23 | $candidateValue = $this.Value[0] 24 | } 25 | if ($valueType.BaseType -eq [Enum]) { 26 | $enumValue = 0 -as $valueType 27 | if ($valueType::TryParse([String]$candidateValue, $true, [Ref]$enumValue)) { 28 | $this.ParsedValue = $enumValue 29 | } 30 | } else { 31 | $this.ParsedValue = $candidateValue -as $securityOptionInfo.ValueType 32 | } 33 | } 34 | 35 | Hidden [Boolean] CompareValue([Object]$ReferenceValue, [Object]$DifferenceValue) { 36 | if ($ReferenceValue -is [Array] -or $DifferenceValue -is [Array]) { 37 | return ($ReferenceValue -join ' ') -eq ($DifferenceValue -join ' ') 38 | } else { 39 | return $ReferenceValue -eq $DifferenceValue 40 | } 41 | } 42 | 43 | [SecurityOption] Get() { 44 | $securityOption = Get-SecurityOption -Name $this.Name 45 | 46 | $this.Name = $securityOption.Name 47 | $this.Value = $securityOption.Value 48 | $this.Description = $securityOption.Description 49 | 50 | return $this 51 | } 52 | 53 | [Void] Set() { 54 | if ($this.Ensure -eq 'Present') { 55 | $this.ParseValue() 56 | 57 | Set-SecurityOption -Name $this.Name -Value $this.ParsedValue 58 | } elseif ($this.Ensure -eq 'Absent') { 59 | Reset-SecurityOption -Name $this.Name 60 | } 61 | } 62 | 63 | [Boolean] Test() { 64 | $securityOption = Get-SecurityOption -Name $this.Name 65 | 66 | if ($this.Ensure -eq 'Present') { 67 | $this.ParseValue() 68 | 69 | return $this.CompareValue($this.ParsedValue, $securityOption.Value) 70 | } elseif ($this.Ensure -eq 'Absent') { 71 | $securityOptionInfo = Resolve-SecurityOption $this.Name 72 | 73 | return $this.CompareValue($securityOptionInfo.Default, $securityOption.Value) 74 | } 75 | 76 | return $true 77 | } 78 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/public/Resolve-SecurityOption.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe Resolve-SecurityOption { 19 | It 'Resolves data for the localized policy ' -TestCases $( 20 | foreach ($name in $Script:securityOptionData.Keys) { 21 | @{ Name = $name } 22 | } 23 | ) { 24 | param ( 25 | $Name 26 | ) 27 | 28 | $Script:securityOptionData[$Name].Description | Should -Not -BeNullOrEmpty 29 | } 30 | 31 | It 'Gets option information based on the short name' { 32 | $securityOptionInfo = Resolve-SecurityOption 'EnableLUA' 33 | 34 | $securityOptionInfo | Should -Not -BeNullOrEmpty 35 | $securityOptionInfo.Name | Should -Be 'EnableLUA' 36 | $securityOptionInfo.Default | Should -Be 'Enabled' 37 | } 38 | 39 | It 'Gets option information based on the description' { 40 | $securityOptionInfo = Resolve-SecurityOption 'User Account Control: Run all administrators in Admin Approval Mode' 41 | 42 | $securityOptionInfo | Should -Not -BeNullOrEmpty 43 | $securityOptionInfo.Name | Should -Be 'EnableLUA' 44 | $securityOptionInfo.Default | Should -Be 'Enabled' 45 | } 46 | 47 | It 'Finds option information based on the description when a wildcard is used' { 48 | $securityOptionInfo = Resolve-SecurityOption 'User*' 49 | 50 | $securityOptionInfo | Should -Not -BeNullOrEmpty 51 | $securityOptionInfo.Name | Should -Contain 'EnableLUA' 52 | } 53 | 54 | It 'Finds option information based on the category' { 55 | $securityOptionInfo = Resolve-SecurityOption -Category 'User Account Control' 56 | 57 | $securityOptionInfo | Should -Not -BeNullOrEmpty 58 | $securityOptionInfo.Name | Should -Contain 'EnableLUA' 59 | } 60 | 61 | It 'When an invalid option is passed, throws an error' { 62 | { Resolve-SecurityOption 'Invalid' } | Should -Throw -ErrorId 'CannotResolveSecurityOption' 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/help/Reset-SecurityOption.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Indented.SecurityPolicy-help.xml 3 | Module Name: Indented.SecurityPolicy 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Reset-SecurityOption 9 | 10 | ## SYNOPSIS 11 | Reset the value of a security option to its default. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Reset-SecurityOption [-Name] [-WhatIf] [-Confirm] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | Reset the value of a security option to its default. 21 | 22 | ## EXAMPLES 23 | 24 | ### EXAMPLE 1 25 | ``` 26 | Reset-SecurityOption FIPSAlgorithmPolicy 27 | ``` 28 | 29 | Resets the FIPSAlgorithmPolicy policy to the default value, Disabled. 30 | 31 | ### EXAMPLE 2 32 | ``` 33 | Reset-SecurityOption 'Interactive logon: Message text for users attempting to log on' 34 | ``` 35 | 36 | Resets the LegalNoticeText policy to an empty string. 37 | 38 | ### EXAMPLE 3 39 | ``` 40 | Reset-SecurityOption ForceKeyProtection 41 | ``` 42 | 43 | Resets the ForceKeyProtection policy to "Not Defined" by removing the associated registry value. 44 | 45 | ## PARAMETERS 46 | 47 | ### -Name 48 | The name of the security option to set. 49 | 50 | ```yaml 51 | Type: String[] 52 | Parameter Sets: (All) 53 | Aliases: 54 | 55 | Required: True 56 | Position: 2 57 | Default value: None 58 | Accept pipeline input: True (ByPropertyName, ByValue) 59 | Accept wildcard characters: False 60 | ``` 61 | 62 | ### -WhatIf 63 | Shows what would happen if the cmdlet runs. 64 | The cmdlet is not run. 65 | 66 | ```yaml 67 | Type: SwitchParameter 68 | Parameter Sets: (All) 69 | Aliases: wi 70 | 71 | Required: False 72 | Position: Named 73 | Default value: None 74 | Accept pipeline input: False 75 | Accept wildcard characters: False 76 | ``` 77 | 78 | ### -Confirm 79 | Prompts you for confirmation before running the cmdlet. 80 | 81 | ```yaml 82 | Type: SwitchParameter 83 | Parameter Sets: (All) 84 | Aliases: cf 85 | 86 | Required: False 87 | Position: Named 88 | Default value: None 89 | Accept pipeline input: False 90 | Accept wildcard characters: False 91 | ``` 92 | 93 | ### CommonParameters 94 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 95 | 96 | ## INPUTS 97 | 98 | ## OUTPUTS 99 | 100 | ## NOTES 101 | 102 | ## RELATED LINKS 103 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/help/Grant-UserRight.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Indented.SecurityPolicy-help.xml 3 | Module Name: Indented.SecurityPolicy 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Grant-UserRight 9 | 10 | ## SYNOPSIS 11 | Grant rights to an account. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Grant-UserRight [-Name] [-AccountName] [-WhatIf] [-Confirm] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | Grants one or more rights to the specified accounts. 21 | 22 | ## EXAMPLES 23 | 24 | ### EXAMPLE 1 25 | ``` 26 | Grant-UserRight -Name 'Allow logon locally' -AccountName 'Administrators' 27 | ``` 28 | 29 | Grants the allow logon locally right to the Administrators group. 30 | 31 | ## PARAMETERS 32 | 33 | ### -Name 34 | The user right, or rights, to query. 35 | 36 | ```yaml 37 | Type: String[] 38 | Parameter Sets: (All) 39 | Aliases: UserRight 40 | 41 | Required: True 42 | Position: 2 43 | Default value: None 44 | Accept pipeline input: True (ByPropertyName) 45 | Accept wildcard characters: False 46 | ``` 47 | 48 | ### -AccountName 49 | Grant rights to the specified accounts. 50 | Each account may be a string, an NTAccount object, or a SecurityIdentifier object. 51 | 52 | ```yaml 53 | Type: Object[] 54 | Parameter Sets: (All) 55 | Aliases: 56 | 57 | Required: True 58 | Position: 3 59 | Default value: None 60 | Accept pipeline input: True (ByPropertyName, ByValue) 61 | Accept wildcard characters: False 62 | ``` 63 | 64 | ### -WhatIf 65 | Shows what would happen if the cmdlet runs. 66 | The cmdlet is not run. 67 | 68 | ```yaml 69 | Type: SwitchParameter 70 | Parameter Sets: (All) 71 | Aliases: wi 72 | 73 | Required: False 74 | Position: Named 75 | Default value: None 76 | Accept pipeline input: False 77 | Accept wildcard characters: False 78 | ``` 79 | 80 | ### -Confirm 81 | Prompts you for confirmation before running the cmdlet. 82 | 83 | ```yaml 84 | Type: SwitchParameter 85 | Parameter Sets: (All) 86 | Aliases: cf 87 | 88 | Required: False 89 | Position: Named 90 | Default value: None 91 | Accept pipeline input: False 92 | Accept wildcard characters: False 93 | ``` 94 | 95 | ### CommonParameters 96 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 97 | 98 | ## INPUTS 99 | 100 | ## OUTPUTS 101 | 102 | ## NOTES 103 | 104 | ## RELATED LINKS 105 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/help/Set-UserRight.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Indented.SecurityPolicy-help.xml 3 | Module Name: Indented.SecurityPolicy 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Set-UserRight 9 | 10 | ## SYNOPSIS 11 | Set the value of a user rights assignment to the specified list of principals. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Set-UserRight [-Name] [-AccountName] [-WhatIf] [-Confirm] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | Set the value of a user rights assignment to the specified list of principals, replacing any existing entries. 21 | 22 | ## EXAMPLES 23 | 24 | ### EXAMPLE 1 25 | ``` 26 | Set-UserRight -Name SeShutdownPrivilege -AccountName 'Administrators' 27 | ``` 28 | 29 | Replaces the accounts granted the SeShutdownPrivilege right with Administrators. 30 | 31 | ## PARAMETERS 32 | 33 | ### -Name 34 | The user right, or rights, to query. 35 | 36 | ```yaml 37 | Type: String[] 38 | Parameter Sets: (All) 39 | Aliases: UserRight 40 | 41 | Required: True 42 | Position: 2 43 | Default value: None 44 | Accept pipeline input: True (ByPropertyName) 45 | Accept wildcard characters: False 46 | ``` 47 | 48 | ### -AccountName 49 | The list of identities which should set for the specified policy. 50 | 51 | ```yaml 52 | Type: Object[] 53 | Parameter Sets: (All) 54 | Aliases: 55 | 56 | Required: True 57 | Position: 3 58 | Default value: None 59 | Accept pipeline input: True (ByPropertyName) 60 | Accept wildcard characters: False 61 | ``` 62 | 63 | ### -WhatIf 64 | Shows what would happen if the cmdlet runs. 65 | The cmdlet is not run. 66 | 67 | ```yaml 68 | Type: SwitchParameter 69 | Parameter Sets: (All) 70 | Aliases: wi 71 | 72 | Required: False 73 | Position: Named 74 | Default value: None 75 | Accept pipeline input: False 76 | Accept wildcard characters: False 77 | ``` 78 | 79 | ### -Confirm 80 | Prompts you for confirmation before running the cmdlet. 81 | 82 | ```yaml 83 | Type: SwitchParameter 84 | Parameter Sets: (All) 85 | Aliases: cf 86 | 87 | Required: False 88 | Position: Named 89 | Default value: None 90 | Accept pipeline input: False 91 | Accept wildcard characters: False 92 | ``` 93 | 94 | ### CommonParameters 95 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 96 | 97 | ## INPUTS 98 | 99 | ## OUTPUTS 100 | 101 | ## NOTES 102 | 103 | ## RELATED LINKS 104 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/help/Set-SecurityOption.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Indented.SecurityPolicy-help.xml 3 | Module Name: Indented.SecurityPolicy 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Set-SecurityOption 9 | 10 | ## SYNOPSIS 11 | Set the value of a security option. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Set-SecurityOption [-Name] [-Value] [-WhatIf] [-Confirm] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | Set the value of a security option. 21 | 22 | ## EXAMPLES 23 | 24 | ### EXAMPLE 1 25 | ``` 26 | Set-SecurityOption EnableLUA Enabled 27 | ``` 28 | 29 | Enables the "User Account Control: Run all administrators in Admin Approval Mode" policy. 30 | 31 | ### EXAMPLE 2 32 | ``` 33 | Set-SecurityOption LegalNoticeText '' 34 | ``` 35 | 36 | Sets the value of the LegalNoticeText policy to an empty string. 37 | 38 | ### EXAMPLE 3 39 | ``` 40 | 41 | ``` 42 | 43 | ## PARAMETERS 44 | 45 | ### -Name 46 | The name of the security option to set. 47 | 48 | ```yaml 49 | Type: String 50 | Parameter Sets: (All) 51 | Aliases: 52 | 53 | Required: True 54 | Position: 2 55 | Default value: None 56 | Accept pipeline input: True (ByPropertyName, ByValue) 57 | Accept wildcard characters: False 58 | ``` 59 | 60 | ### -Value 61 | The value to set. 62 | 63 | ```yaml 64 | Type: Object 65 | Parameter Sets: (All) 66 | Aliases: 67 | 68 | Required: True 69 | Position: 3 70 | Default value: None 71 | Accept pipeline input: True (ByPropertyName) 72 | Accept wildcard characters: False 73 | ``` 74 | 75 | ### -WhatIf 76 | Shows what would happen if the cmdlet runs. 77 | The cmdlet is not run. 78 | 79 | ```yaml 80 | Type: SwitchParameter 81 | Parameter Sets: (All) 82 | Aliases: wi 83 | 84 | Required: False 85 | Position: Named 86 | Default value: None 87 | Accept pipeline input: False 88 | Accept wildcard characters: False 89 | ``` 90 | 91 | ### -Confirm 92 | Prompts you for confirmation before running the cmdlet. 93 | 94 | ```yaml 95 | Type: SwitchParameter 96 | Parameter Sets: (All) 97 | Aliases: cf 98 | 99 | Required: False 100 | Position: Named 101 | Default value: None 102 | Accept pipeline input: False 103 | Accept wildcard characters: False 104 | ``` 105 | 106 | ### CommonParameters 107 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 108 | 109 | ## INPUTS 110 | 111 | ## OUTPUTS 112 | 113 | ## NOTES 114 | 115 | ## RELATED LINKS 116 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityPolicy/SecurityPolicy.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {3A256B08-855C-414F-9E5D-3EE28E7B0FDE} 8 | Library 9 | Properties 10 | SecurityPolicy 11 | SecurityPolicy 12 | v4.5.2 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/en/userRights.psd1: -------------------------------------------------------------------------------- 1 | ConvertFrom-StringData @' 2 | SeTrustedCredManAccessPrivilege = Access Credential Manager as a trusted caller 3 | SeNetworkLogonRight = Access this computer from the network 4 | SeTcbPrivilege = Act as part of the operating system 5 | SeMachineAccountPrivilege = Add workstations to domain 6 | SeIncreaseQuotaPrivilege = Adjust memory quotas for a process 7 | SeInteractiveLogonRight = Allow log on locally 8 | SeRemoteInteractiveLogonRight = Allow log on through Remote Desktop Services 9 | SeBackupPrivilege = Back up files and directories 10 | SeChangeNotifyPrivilege = Bypass traverse checking 11 | SeSystemtimePrivilege = Change the system time 12 | SeTimeZonePrivilege = Change the time zone 13 | SeCreatePagefilePrivilege = Create a pagefile 14 | SeCreateTokenPrivilege = Create a token object 15 | SeCreateGlobalPrivilege = Create global objects 16 | SeCreatePermanentPrivilege = Create permanent shared objects 17 | SeCreateSymbolicLinkPrivilege = Create symbolic links 18 | SeDebugPrivilege = Debug programs 19 | SeDenyNetworkLogonRight = Deny access this computer from the network 20 | SeDenyBatchLogonRight = Deny log on as a batch job 21 | SeDenyServiceLogonRight = Deny log on as a service 22 | SeDenyInteractiveLogonRight = Deny log on locally 23 | SeDenyRemoteInteractiveLogonRight = Deny log on through Remote Desktop Services 24 | SeEnableDelegationPrivilege = Enable computer and user accounts to be trusted for delegation 25 | SeRemoteShutdownPrivilege = Force shutdown from a remote system 26 | SeAuditPrivilege = Generate security audits 27 | SeImpersonatePrivilege = Impersonate a client after authentication 28 | SeIncreaseWorkingSetPrivilege = Increase a process working set 29 | SeIncreaseBasePriorityPrivilege = Increase scheduling priority 30 | SeLoadDriverPrivilege = Load and unload device drivers 31 | SeLockMemoryPrivilege = Lock pages in memory 32 | SeBatchLogonRight = Log on as a batch job 33 | SeServiceLogonRight = Log on as a service 34 | SeSecurityPrivilege = Manage auditing and security log 35 | SeRelabelPrivilege = Modify an object label 36 | SeSystemEnvironmentPrivilege = Modify firmware environment values 37 | SeManageVolumePrivilege = Perform volume maintenance tasks 38 | SeProfileSingleProcessPrivilege = Profile single process 39 | SeSystemProfilePrivilege = Profile system performance 40 | SeUndockPrivilege = Remove computer from docking station 41 | SeAssignPrimaryTokenPrivilege = Replace a process level token 42 | SeRestorePrivilege = Restore files and directories 43 | SeShutdownPrivilege = Shut down the system 44 | SeSyncAgentPrivilege = Synchronize directory service data 45 | SeTakeOwnershipPrivilege = Take ownership of files or other objects 46 | SeDelegateSessionUserImpersonatePrivilege = Obtain an impersonation token for another user in the same session 47 | '@ -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityPolicy/ServiceAccount.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace Indented.SecurityPolicy 5 | { 6 | public class ServiceAccount 7 | { 8 | const uint STATUS_ACCESS_DENIED = 0xc0000022; 9 | const uint STATUS_INSUFFICIENT_RESOURCES = 0xc000009a; 10 | const uint STATUS_NO_MEMORY = 0xc0000017; 11 | const uint STATUS_OBJECT_NAME_NOT_FOUND = 0xc0000034; 12 | const uint STATUS_NO_MORE_ENTRIES = 0x8000001a; 13 | 14 | public static void AddServiceAccount(string AccountName) 15 | { 16 | try 17 | { 18 | uint ntStatus = UnsafeNativeMethods.NetAddServiceAccount( 19 | null, 20 | AccountName, 21 | null, 22 | 0 23 | ); 24 | TestNtStatus(ntStatus); 25 | } 26 | catch 27 | { 28 | throw; 29 | } 30 | } 31 | 32 | public static bool IsServiceAccount(string AccountName) 33 | { 34 | try 35 | { 36 | bool isService; 37 | uint ntStatus = UnsafeNativeMethods.NetIsServiceAccount( 38 | null, 39 | AccountName, 40 | out isService 41 | ); 42 | TestNtStatus(ntStatus); 43 | 44 | return isService; 45 | } 46 | catch 47 | { 48 | throw; 49 | } 50 | } 51 | 52 | public static void RemoveServiceAccount(string AccountName) 53 | { 54 | try 55 | { 56 | uint ntStatus = UnsafeNativeMethods.NetAddServiceAccount( 57 | null, 58 | AccountName, 59 | null, 60 | 0 61 | ); 62 | TestNtStatus(ntStatus); 63 | } 64 | catch 65 | { 66 | throw; 67 | } 68 | } 69 | 70 | private static bool TestNtStatus(uint ntStatus) 71 | { 72 | switch (ntStatus) 73 | { 74 | case 0: 75 | return true; 76 | case STATUS_NO_MORE_ENTRIES: 77 | return true; 78 | case STATUS_ACCESS_DENIED: 79 | throw new UnauthorizedAccessException(); 80 | case STATUS_INSUFFICIENT_RESOURCES: 81 | throw new OutOfMemoryException(); 82 | case STATUS_NO_MEMORY: 83 | throw new OutOfMemoryException(); 84 | default: 85 | throw new Win32Exception(UnsafeNativeMethods.LsaNtStatusToWinError((int)ntStatus)); 86 | } 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/public/Get-UserRight.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe Get-UserRight { 19 | BeforeAll { 20 | Mock CloseLsaPolicy 21 | Mock OpenLsaPolicy { 22 | [PSCustomObject]@{} | 23 | Add-Member EnumerateAccountsWithUserRight -MemberType ScriptMethod -PassThru -Value { 24 | 'username' 25 | } 26 | } 27 | Mock Resolve-UserRight { 28 | [PSCustomObject]@{ 29 | Name = 'SeDenyServiceLogonRight' 30 | Description = 'Deny log on as a service' 31 | } 32 | } 33 | 34 | $defaultParams = @{ 35 | Name = 'SeDenyServiceLogonRight' 36 | } 37 | } 38 | 39 | Context 'Accounts are assigned to the right' { 40 | It 'When one or more accounts is assigned to the right, returns the list of accounts' { 41 | $result = Get-UserRight @defaultParams 42 | 43 | Assert-MockCalled OpenLsaPolicy -Scope It -Times 1 44 | Assert-MockCalled CloseLsaPolicy -Scope It -Times 1 45 | 46 | $result | Should -Not -BeNullOrEmpty 47 | @($result).Count | Should -Be 1 48 | $result.Name | Should -Be 'SeDenyServiceLogonRight' 49 | $result.Description | Should -Be 'Deny log on as a service' 50 | $result.AccountName | Should -Be 'username' 51 | } 52 | } 53 | 54 | Context 'Error handling - EnumerateAccountsWithUserRight' { 55 | BeforeAll { 56 | Mock OpenLsaPolicy { 57 | [PSCustomObject]@{} | 58 | Add-Member EnumerateAccountsWithUserRight -MemberType ScriptMethod -PassThru -Value { 59 | throw 'enumerate error' 60 | } 61 | } 62 | } 63 | 64 | It 'When EnumerateAccountsWithUserRight throws, writes an error' { 65 | { Get-UserRight @defaultParams -ErrorAction Stop } | Should -Throw 'enumerate error' 66 | { Get-UserRight @defaultParams -ErrorAction SilentlyContinue } | Should -Not -Throw 67 | 68 | Assert-MockCalled OpenLsaPolicy -Scope It -Times 2 69 | Assert-MockCalled CloseLsaPolicy -Scope It -Times 1 70 | } 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/public/Resolve-SecurityOption.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Management.Automation 2 | 3 | function Resolve-SecurityOption { 4 | <# 5 | .SYNOPSIS 6 | Resolves the name of a security option as shown in the local security policy editor. 7 | .DESCRIPTION 8 | Resolves the name of a security option as shown in the local security policy editor to either the registry value name, or the name of an implementing class. 9 | .EXAMPLE 10 | Resolve-SecurityOption "User Account Control: Run all administrators in Admin Approval Mode" 11 | 12 | Returns information about the security option. 13 | #> 14 | 15 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] 16 | [CmdletBinding()] 17 | param ( 18 | # The name or description of a user right. Wildcards are supported for description values. 19 | [Parameter(Position = 1, ValueFromPipeline, ParameterSetName = 'ByName')] 20 | [String]$Name, 21 | 22 | # Get the policies under a specific category, for example "Network security". 23 | [Parameter(Mandatory, ParameterSetName = 'ByCategory')] 24 | [ArgumentCompleter( { 25 | param ( 26 | [String]$CommandName, 27 | [String]$ParameterName, 28 | [String]$WordToComplete 29 | ) 30 | 31 | [System.Collections.Generic.HashSet[String]](Resolve-SecurityOption).Category -like "$WordToComplete*" 32 | } )] 33 | [String]$Category 34 | ) 35 | 36 | process { 37 | if ($Name) { 38 | if ($Script:securityOptionData.Contains($Name)) { 39 | $Script:securityOptionData[$Name] 40 | } elseif ($Script:securityOptionLookupHelper.Contains($Name)) { 41 | $Script:securityOptionData[$Script:securityOptionLookupHelper[$Name]] 42 | } else { 43 | $isLikeDescription = $false 44 | foreach ($value in $Script:securityOptionLookupHelper.Keys -like $Name) { 45 | $isLikeDescription = $true 46 | $Script:securityOptionData[$Script:securityOptionLookupHelper[$value]] 47 | } 48 | if (-not $isLikeDescription) { 49 | $errorRecord = [ErrorRecord]::new( 50 | [ArgumentException]::new('"{0}" does not resolve to a security option' -f $Name), 51 | 'CannotResolveSecurityOption', 52 | 'InvalidArgument', 53 | $Name 54 | ) 55 | $pscmdlet.ThrowTerminatingError($errorRecord) 56 | } 57 | } 58 | } elseif ($Category) { 59 | $Script:securityOptionData.Values.Where{ $_.Category -like $Category } 60 | } else { 61 | $Script:securityOptionData.Values 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/public/Grant-UserRight.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe Grant-UserRight { 19 | BeforeAll { 20 | Mock CloseLsaPolicy 21 | Mock OpenLsaPolicy { 22 | [PSCustomObject]@{} | 23 | Add-Member AddAccountRights -MemberType ScriptMethod -PassThru -Value { 24 | $Script:addWasCalled++ 25 | } 26 | } 27 | Mock Resolve-UserRight { 28 | [PSCustomObject]@{ 29 | Name = 'SeDenyServiceLogonRight' 30 | } 31 | } 32 | 33 | $defaultParams = @{ 34 | AccountName = 'username' 35 | Name = 'SeDenyServiceLogonRight' 36 | } 37 | } 38 | 39 | BeforeEach { 40 | $Script:addWasCalled = 0 41 | } 42 | 43 | Context 'Add an account to a right' { 44 | It 'Given a account to add to a right, calls AddAccountRights' { 45 | Grant-UserRight @defaultParams 46 | 47 | Assert-MockCalled OpenLsaPolicy -Scope It -Times 1 48 | Assert-MockCalled CloseLsaPolicy -Scope It -Times 1 49 | 50 | $Script:addWasCalled | Should -Be 1 51 | } 52 | } 53 | 54 | Context 'ShouldProcess' { 55 | It 'When WhatIf is used, does not call AddAccountRights' { 56 | Grant-UserRight @defaultParams -WhatIf 57 | 58 | Assert-MockCalled OpenLsaPolicy -Scope It -Times 1 59 | Assert-MockCalled CloseLsaPolicy -Scope It -Times 1 60 | 61 | $Script:addWasCalled | Should -Be 0 62 | } 63 | } 64 | 65 | Context 'Error handling - AddAccountRights' { 66 | BeforeAll { 67 | Mock OpenLsaPolicy { 68 | [PSCustomObject]@{} | 69 | Add-Member AddAccountRights -MemberType ScriptMethod -PassThru -Value { 70 | throw 'add error' 71 | } 72 | } 73 | } 74 | 75 | It 'When AddAccountRights throws, writes an error' { 76 | { Grant-UserRight @defaultParams -ErrorAction Stop } | Should -Throw 'add error' 77 | { Grant-UserRight @defaultParams -ErrorAction SilentlyContinue } | Should -Not -Throw 78 | 79 | Assert-MockCalled OpenLsaPolicy -Scope It -Times 2 80 | Assert-MockCalled CloseLsaPolicy -Scope It -Times 1 81 | } 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/securityoptions/AccountStatus.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe AccountStatus { 19 | BeforeAll { 20 | Mock GetIadsLocalUser { 21 | [PSCustomObject]@{ 22 | Enabled = $true 23 | } 24 | } 25 | Mock EnableIadsLocalUser 26 | Mock DisableIadsLocalUser 27 | } 28 | 29 | BeforeEach { 30 | $class = [AccountStatus]@{ 31 | SidType = 'AccountGuestSid' 32 | Value = 'Enabled' 33 | } 34 | } 35 | 36 | Context 'Get' { 37 | It 'Gets the current status of the account' { 38 | $class.Value = 'Disabled' 39 | $instance = $class.Get() 40 | $instance.Value | Should -Be 'Enabled' 41 | } 42 | } 43 | 44 | Context 'Set' { 45 | It 'When the account should be enabled, calls EnableIadsLocalUser' { 46 | $class.Set() 47 | 48 | Assert-MockCalled EnableIadsLocalUser -Times 1 -Scope It 49 | Assert-MockCalled DisableIadsLocalUser -Times 0 -Scope It 50 | } 51 | 52 | It 'When the account should be disabled, calls DisableIadsLocalUser' { 53 | $class.Value = 'Disabled' 54 | $class.Set() 55 | 56 | Assert-MockCalled EnableIadsLocalUser -Times 0 -Scope It 57 | Assert-MockCalled DisableIadsLocalUser -Times 1 -Scope It 58 | } 59 | } 60 | 61 | Context 'Test, account is enabled' { 62 | It 'When the account should be enabled, returns true' { 63 | $class.Test() | Should -Be $true 64 | } 65 | 66 | It 'When the account should be disabled, returns false' { 67 | $class.Value = 'Disabled' 68 | 69 | $class.Test() | Should -Be $false 70 | } 71 | } 72 | 73 | Context 'Test, account is disabled' { 74 | BeforeAll { 75 | Mock GetIadsLocalUser { 76 | [PSCustomObject]@{ 77 | Enabled = $false 78 | } 79 | } 80 | } 81 | 82 | It 'When the account should be disabled, returns true' { 83 | $class.Value = 'Disabled' 84 | 85 | $class.Test() | Should -Be $true 86 | } 87 | 88 | It 'When the account should be enabled, returns false' { 89 | $class.Test() | Should -Be $false 90 | } 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/private/ImportUserRightData.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe ImportUserRightData { 19 | AfterAll { 20 | ImportUserRightData 21 | } 22 | 23 | Context 'Import' { 24 | BeforeAll { 25 | Mock Import-LocalizedData { 26 | Set-Variable $BindingVariable -Scope Script -Value @{ 27 | SeTrustedCredManAccessPrivilege = 'Access Credential Manager as a trusted caller' 28 | SeNetworkLogonRight = 'Access this computer from the network' 29 | } 30 | } 31 | } 32 | 33 | BeforeEach { 34 | $Script:userRightData = $null 35 | $Script:userRightLookupHelper = $null 36 | } 37 | 38 | It 'Fills a module scoped hashtable with values' { 39 | ImportUserRightData 40 | 41 | $Script:userRightData | Should -Not -BeNullOrEmpty 42 | $Script:userRightLookupHelper | Should -Not -BeNullOrEmpty 43 | } 44 | 45 | It 'Converts the description "" to the constant name ""' -TestCases @( 46 | @{ Description = 'Access Credential Manager as a trusted caller'; Name = 'SeTrustedCredManAccessPrivilege' } 47 | @{ Description = 'Access this computer from the network'; Name = 'SeNetworkLogonRight' } 48 | ) { 49 | param ( $Description, $Name ) 50 | 51 | ImportUserRightData 52 | 53 | $Script:userRightLookupHelper.Contains($Description) | Should -Be $true 54 | $Script:userRightLookupHelper[$Description] | Should -Be $Name 55 | } 56 | 57 | It 'Handles culture fallback' -TestCases @( 58 | @{ Culture = 'en-US' } 59 | @{ Culture = 'en-GB' } 60 | @{ Culture = 'da-DK' } 61 | @{ Culture = 'da' } 62 | @{ Culture = 'sv-FI' } 63 | ) { 64 | param ( 65 | $Culture 66 | ) 67 | 68 | $currentCulture = $PSUICulture 69 | 70 | & { 71 | [System.Threading.Thread]::CurrentThread.CurrentUICulture = $Culture 72 | 73 | ImportUserRightData 74 | 75 | $Description = 'Access Credential Manager as a trusted caller' 76 | $Name = 'SeTrustedCredManAccessPrivilege' 77 | 78 | $Script:userRightLookupHelper[$Description] | Should -Be $Name 79 | 80 | [System.Threading.Thread]::CurrentThread.CurrentUICulture = $currentCulture 81 | } 82 | } 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/public/Get-SecurityOption.ps1: -------------------------------------------------------------------------------- 1 | function Get-SecurityOption { 2 | <# 3 | .SYNOPSIS 4 | Get the value of a security option. 5 | .DESCRIPTION 6 | Get the value of a security option. 7 | .EXAMPLE 8 | Get-SecurityOption 'Accounts: Administrator account status' 9 | 10 | Gets the current value of the administrator account status policy (determined by the state of the account). 11 | .EXAMPLE 12 | Get-SecurityOption 'EnableLUA' 13 | 14 | Get the value of the "User Account Control: Run all administrators in Admin Approval Mode" policy. 15 | #> 16 | 17 | [CmdletBinding()] 18 | param ( 19 | # The name of the security option to set. 20 | [Parameter(Position = 1, ValueFromPipeline, ValueFromPipelineByPropertyName)] 21 | [String[]]$Name 22 | ) 23 | 24 | process { 25 | foreach ($securityOptionInfo in $Name | Resolve-SecurityOption | Sort-Object Category, ShortDescription) { 26 | try { 27 | $value = $securityOptionInfo.Default 28 | 29 | if ($securityOptionInfo.Key) { 30 | Write-Debug ('Registry value type: {0}' -f $securityOption.ValueName) 31 | 32 | if (Test-Path $securityOptionInfo.Key) { 33 | $key = Get-Item $securityOptionInfo.Key -ErrorAction Stop 34 | 35 | if ($key.GetValueNames() -contains $securityOptionInfo.Name) { 36 | $value = Get-ItemPropertyValue -Path $securityOptionInfo.Key -Name $securityOptionInfo.Name -ErrorAction Stop 37 | } 38 | } 39 | } else { 40 | Write-Debug ('Class-handled value type: {0}' -f $securityOptionInfo.Name) 41 | 42 | $class = NewImplementingType $securityOptionInfo.Class 43 | $value = $class.Get().Value 44 | } 45 | 46 | if ($value -ne 'Not Defined') { 47 | $value = $value -as ($securityOptionInfo.ValueType -as [Type]) 48 | } 49 | 50 | [PSCustomObject]@{ 51 | Name = $securityOptionInfo.Name 52 | Description = $securityOptionInfo.Name 53 | Value = $value 54 | Category = $securityOptionInfo.Category 55 | ShortDescription = $securityOptionInfo.ShortDescription 56 | PSTypeName = 'Indented.SecurityPolicy.SecurityOptionSetting' 57 | } 58 | } catch { 59 | $innerException = $_.Exception.GetBaseException() 60 | $errorRecord = [ErrorRecord]::new( 61 | [InvalidOperationException]::new( 62 | ('An error occurred retrieving the security option {0}: {1}' -f $securityOptionInfo.ValueName, $innerException.Message), 63 | $innerException 64 | ), 65 | 'FailedToRetrieveSecurityOptionSetting', 66 | 'OperationStopped', 67 | $null 68 | ) 69 | Write-Error -ErrorRecord $errorRecord 70 | } 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/help/Revoke-UserRight.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Indented.SecurityPolicy-help.xml 3 | Module Name: Indented.SecurityPolicy 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Revoke-UserRight 9 | 10 | ## SYNOPSIS 11 | Revokes rights granted to an account. 12 | 13 | ## SYNTAX 14 | 15 | ### List (Default) 16 | ``` 17 | Revoke-UserRight [-Name] [-AccountName] [-WhatIf] [-Confirm] [] 18 | ``` 19 | 20 | ### All 21 | ``` 22 | Revoke-UserRight [-AccountName] [-AllRights] [-WhatIf] [-Confirm] [] 23 | ``` 24 | 25 | ## DESCRIPTION 26 | Revokes the rights granted to an account or set of accounts. 27 | 28 | The All switch may be used to revoke all rights granted to the specified accounts. 29 | 30 | ## EXAMPLES 31 | 32 | ### EXAMPLE 1 33 | ``` 34 | Revoke-UserRight -Name 'Log on as a service' -AccountName 'JonDoe' 35 | ``` 36 | 37 | Revokes the logon as a service right granted to the account named JonDoe. 38 | 39 | ### EXAMPLE 2 40 | ``` 41 | Revoke-UserRight -AccountName 'JonDoe' -All 42 | ``` 43 | 44 | Revokes all rights which have been granted to the identity JonDoe. 45 | 46 | ## PARAMETERS 47 | 48 | ### -Name 49 | The user right, or rights, to query. 50 | 51 | ```yaml 52 | Type: String[] 53 | Parameter Sets: List 54 | Aliases: UserRight 55 | 56 | Required: True 57 | Position: 2 58 | Default value: None 59 | Accept pipeline input: False 60 | Accept wildcard characters: False 61 | ``` 62 | 63 | ### -AccountName 64 | Grant rights to the specified principals. 65 | The principal may be a string, an NTAccount object, or a SecurityIdentifier object. 66 | 67 | ```yaml 68 | Type: Object[] 69 | Parameter Sets: (All) 70 | Aliases: 71 | 72 | Required: True 73 | Position: 3 74 | Default value: None 75 | Accept pipeline input: False 76 | Accept wildcard characters: False 77 | ``` 78 | 79 | ### -AllRights 80 | Clear all rights granted to the specified accounts. 81 | 82 | ```yaml 83 | Type: SwitchParameter 84 | Parameter Sets: All 85 | Aliases: 86 | 87 | Required: True 88 | Position: Named 89 | Default value: False 90 | Accept pipeline input: False 91 | Accept wildcard characters: False 92 | ``` 93 | 94 | ### -WhatIf 95 | Shows what would happen if the cmdlet runs. 96 | The cmdlet is not run. 97 | 98 | ```yaml 99 | Type: SwitchParameter 100 | Parameter Sets: (All) 101 | Aliases: wi 102 | 103 | Required: False 104 | Position: Named 105 | Default value: None 106 | Accept pipeline input: False 107 | Accept wildcard characters: False 108 | ``` 109 | 110 | ### -Confirm 111 | Prompts you for confirmation before running the cmdlet. 112 | 113 | ```yaml 114 | Type: SwitchParameter 115 | Parameter Sets: (All) 116 | Aliases: cf 117 | 118 | Required: False 119 | Position: Named 120 | Default value: None 121 | Accept pipeline input: False 122 | Accept wildcard characters: False 123 | ``` 124 | 125 | ### CommonParameters 126 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 127 | 128 | ## INPUTS 129 | 130 | ## OUTPUTS 131 | 132 | ## NOTES 133 | 134 | ## RELATED LINKS 135 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/Indented.SecurityPolicy.psm1: -------------------------------------------------------------------------------- 1 | # Note: This module is not functional without the compiled SecurityPolicy solution. This loader cannot be reliably used. 2 | 3 | $enumeration = @( 4 | 'SecurityOptions\AllocateDASD' 5 | 'SecurityOptions\AuditNTLMInDomain' 6 | 'SecurityOptions\AuditReceivingNTLMTraffic' 7 | 'SecurityOptions\ConsentPromptBehaviorAdmin' 8 | 'SecurityOptions\ConsentPromptBehaviorUser' 9 | 'SecurityOptions\DontDisplayLockedUserId' 10 | 'SecurityOptions\ForceGuest' 11 | 'SecurityOptions\ForceKeyProtection' 12 | 'SecurityOptions\LdapClientIntegrity' 13 | 'SecurityOptions\LdapServerIntegrity' 14 | 'SecurityOptions\LmCompatibilityLevel' 15 | 'SecurityOptions\NoConnectedUser' 16 | 'SecurityOptions\NTLMMinSec' 17 | 'SecurityOptions\RestrictNTLMInDomain' 18 | 'SecurityOptions\RestrictReceivingNTLMTraffic' 19 | 'SecurityOptions\RestrictSendingNTLMTraffic' 20 | 'SecurityOptions\ScRemoveOption' 21 | 'SecurityOptions\SmbServerNameHardeningLevel' 22 | 'SecurityOptions\SupportedEncryptionTypes' 23 | 'Enabled' 24 | 'Ensure' 25 | 'RegistryValueType' 26 | ) 27 | 28 | foreach ($file in $enumeration) { 29 | . ("{0}\enum\{1}.ps1" -f $psscriptroot, $file) 30 | } 31 | 32 | $class = @( 33 | 'Dsc\GroupManagedServiceAccount' 34 | 'Dsc\RegistryPolicy' 35 | 'Dsc\SecurityOption' 36 | 'Dsc\UserRightAssignment' 37 | 'SecurityOptions\AccountBase' 38 | 'SecurityOptions\AccountStatus' 39 | 'SecurityOptions\AccountStatusAdministrator' 40 | 'SecurityOptions\AccountStatusGuest' 41 | 'SecurityOptions\RenameAccount' 42 | 'SecurityOptions\RenameAccountAdministrator' 43 | 'SecurityOptions\RenameAccountGuest' 44 | ) 45 | 46 | foreach ($file in $class) { 47 | . ("{0}\class\{1}.ps1" -f $psscriptroot, $file) 48 | } 49 | 50 | $private = @( 51 | 'CloseLsaPolicy' 52 | 'GetMachineSid' 53 | 'GetSecurityOptionData' 54 | 'ImportSecurityOptionData' 55 | 'ImportUserRightData' 56 | 'NewImplementingType' 57 | 'OpenLsaPolicy' 58 | ) 59 | 60 | foreach ($file in $private) { 61 | . ("{0}\private\{1}.ps1" -f $psscriptroot, $file) 62 | } 63 | 64 | $public = @( 65 | 'Clear-UserRight' 66 | 'Get-AssignedUserRight' 67 | 'Get-SecurityOption' 68 | 'Get-UserRight' 69 | 'Grant-UserRight' 70 | 'Install-GroupManagedServiceAccount' 71 | 'Reset-SecurityOption' 72 | 'Resolve-SecurityOption' 73 | 'Resolve-UserRight' 74 | 'Revoke-UserRight' 75 | 'Set-SecurityOption' 76 | 'Set-UserRight' 77 | 'Test-GroupManagedServiceAccount' 78 | 'Uninstall-GroupManagedServiceAccount' 79 | ) 80 | 81 | foreach ($file in $public) { 82 | . ("{0}\public\{1}.ps1" -f $psscriptroot, $file) 83 | } 84 | 85 | $functionsToExport = @( 86 | 'Clear-UserRight' 87 | 'Get-AssignedUserRight' 88 | 'Get-SecurityOption' 89 | 'Get-UserRight' 90 | 'Grant-UserRight' 91 | 'Install-GroupManagedServiceAccount' 92 | 'Reset-SecurityOption' 93 | 'Resolve-SecurityOption' 94 | 'Resolve-UserRight' 95 | 'Revoke-UserRight' 96 | 'Set-SecurityOption' 97 | 'Set-UserRight' 98 | 'Test-GroupManagedServiceAccount' 99 | 'Uninstall-GroupManagedServiceAccount' 100 | ) 101 | Export-ModuleMember -Function $functionsToExport 102 | 103 | . ("{0}\InitializeModule.ps1" -f $psscriptroot) 104 | InitializeModule 105 | 106 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityPolicy/UnsafeNativeMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | 5 | namespace Indented.SecurityPolicy 6 | { 7 | class UnsafeNativeMethods 8 | { 9 | private UnsafeNativeMethods() {} 10 | 11 | [DllImport("advapi32")] 12 | internal static extern int LsaClose(IntPtr PolicyHandle); 13 | 14 | [DllImport("advapi32", CharSet = CharSet.Unicode)] 15 | internal static extern uint LsaAddAccountRights( 16 | IntPtr PolicyHandle, 17 | IntPtr AccountSid, 18 | LSA_UNICODE_STRING[] UserRights, 19 | int CountOfRights 20 | ); 21 | 22 | [DllImport("advapi32", CharSet = CharSet.Unicode)] 23 | internal static extern uint LsaEnumerateAccountRights( 24 | IntPtr PolicyHandle, 25 | IntPtr AccountSid, 26 | out IntPtr UserRights, 27 | out ulong CountOfRights 28 | ); 29 | 30 | [DllImport("advapi32", CharSet = CharSet.Unicode)] 31 | internal static extern uint LsaEnumerateAccountsWithUserRight( 32 | IntPtr PolicyHandle, 33 | LSA_UNICODE_STRING UserRights, 34 | out IntPtr EnumerationBuffer, 35 | out ulong CountReturned 36 | ); 37 | 38 | [DllImport("advapi32")] 39 | internal static extern int LsaFreeMemory(IntPtr Buffer); 40 | 41 | [DllImport("advapi32")] 42 | internal static extern int LsaNtStatusToWinError(int NTSTATUS); 43 | 44 | [DllImport("advapi32", CharSet = CharSet.Unicode)] 45 | internal static extern uint LsaOpenPolicy( 46 | LSA_UNICODE_STRING SystemName, 47 | ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, 48 | int AccessMask, 49 | out IntPtr PolicyHandle 50 | ); 51 | 52 | [DllImport("advapi32", CharSet = CharSet.Unicode)] 53 | internal static extern uint LsaRemoveAccountRights( 54 | IntPtr PolicyHandle, 55 | IntPtr AccountSid, 56 | bool AllRights, 57 | LSA_UNICODE_STRING[] UserRights, 58 | int CountOfRights 59 | ); 60 | 61 | [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 62 | internal static extern bool LookupAccountName( 63 | string lpSystemName, 64 | string lpAccountName, 65 | byte[] Sid, 66 | ref uint cbSid, 67 | StringBuilder ReferencedDomainName, 68 | ref uint cchReferencedDomainName, 69 | out SID_NAME_USE peUse 70 | ); 71 | 72 | [DllImport("netapi32.dll", CharSet = CharSet.Unicode)] 73 | internal static extern uint NetAddServiceAccount( 74 | string ServerName, 75 | string AccountName, 76 | string Reserved, 77 | uint Flags 78 | ); 79 | 80 | [DllImport("netapi32.dll", CharSet = CharSet.Unicode)] 81 | internal static extern uint NetIsServiceAccount( 82 | string ServerName, 83 | string AccountName, 84 | out bool IsService 85 | ); 86 | 87 | [DllImport("netapi32.dll", CharSet = CharSet.Unicode)] 88 | internal static extern uint NetRemoveServiceAccount( 89 | string ServerName, 90 | string AccountName, 91 | int Flags 92 | ); 93 | } 94 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/public/Set-UserRight.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Security.Principal 2 | using namespace Indented.SecurityPolicy 3 | 4 | function Set-UserRight { 5 | <# 6 | .SYNOPSIS 7 | Set the value of a user rights assignment to the specified list of principals. 8 | .DESCRIPTION 9 | Set the value of a user rights assignment to the specified list of principals, replacing any existing entries. 10 | .EXAMPLE 11 | Set-UserRight -Name SeShutdownPrivilege -AccountName 'Administrators' 12 | 13 | Replaces the accounts granted the SeShutdownPrivilege right with Administrators. 14 | #> 15 | 16 | [CmdletBinding(SupportsShouldProcess)] 17 | param ( 18 | # The user right, or rights, to query. 19 | [Parameter(Mandatory, Position = 1, ValueFromPipelineByPropertyName)] 20 | [ValidateScript( { $_ | Resolve-UserRight } )] 21 | [Alias('UserRight')] 22 | [String[]]$Name, 23 | 24 | # The list of identities which should set for the specified policy. 25 | [Parameter(Mandatory, Position = 2, ValueFromPipelineByPropertyName)] 26 | [Object[]]$AccountName 27 | ) 28 | 29 | begin { 30 | try { 31 | $lsa = OpenLsaPolicy 32 | } catch { 33 | $pscmdlet.ThrowTerminatingError($_) 34 | } 35 | } 36 | 37 | process { 38 | try { 39 | # Ensure all identity values are SecurityIdentifier type 40 | $requestedIdentities = foreach ($account in $AccountName) { 41 | $securityIdentifier = switch ($account.GetType()) { 42 | ([String]) { ([NTAccount]$account).Translate([SecurityIdentifier]); break } 43 | ([NTAccount]) { $account.Translate([SecurityIdentifier]); break } 44 | default { $account } 45 | } 46 | 47 | [PSCustomObject]@{ 48 | Value = $account 49 | SecurityIdentifier = $securityIdentifier 50 | } 51 | } 52 | 53 | foreach ($userRight in $Name | Resolve-UserRight) { 54 | # Get the current value and ensure all returned values are SecurityIdentifier type 55 | $currentIdentities = foreach ($account in $lsa.EnumerateAccountsWithUserRight($userRight.Name)) { 56 | $securityIdentifier = if ($account -is [NTAccount]) { 57 | $account.Translate([SecurityIdentifier]) 58 | } else { 59 | $account 60 | } 61 | 62 | [PSCustomObject]@{ 63 | Value = $account 64 | SecurityIdentifier = $securityIdentifier 65 | } 66 | } 67 | 68 | foreach ($account in Compare-Object @($requestedIdentities) @($currentIdentities) -Property SecurityIdentifier -PassThru) { 69 | if ($account.SideIndicator -eq '<=') { 70 | if ($pscmdlet.ShouldProcess(('Adding {0} to user right {1}' -f $account.Value, $userRight.Name))) { 71 | $lsa.AddAccountRights($account.SecurityIdentifier, $userRight.Name) 72 | } 73 | } elseif ($account.SideIndicator -eq '=>') { 74 | if ($pscmdlet.ShouldProcess(('Removing {0} from user right {1}' -f $account.Value, $userRight.Name))) { 75 | $lsa.RemoveAccountRights($account.SecurityIdentifier, [UserRight[]]$userRight.Name) 76 | } 77 | } 78 | } 79 | } 80 | } catch { 81 | Write-Error -ErrorRecord $_ 82 | } 83 | } 84 | 85 | end { 86 | CloseLsaPolicy $lsa 87 | } 88 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/public/Revoke-UserRight.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe Revoke-UserRight { 19 | BeforeAll { 20 | Mock CloseLsaPolicy 21 | Mock OpenLsaPolicy { 22 | [PSCustomObject]@{} | 23 | Add-Member RemoveAccountRights -MemberType ScriptMethod -PassThru -Value { 24 | $Script:removeWasCalled++ 25 | } | 26 | Add-Member RemoveAllAccountRights -MemberType ScriptMethod -PassThru -Value { 27 | $Script:removeAllWasCalled++ 28 | } 29 | } 30 | Mock Resolve-UserRight { 31 | [PSCustomObject]@{ 32 | Name = 'SeDenyServiceLogonRight' 33 | } 34 | } 35 | 36 | $defaultParams = @{ 37 | AccountName = 'username' 38 | Name = 'SeDenyServiceLogonRight' 39 | } 40 | } 41 | 42 | BeforeEach { 43 | $Script:removeWasCalled = 0 44 | $Script:removeAllWasCalled = 0 45 | } 46 | 47 | Context 'Removes an account from a right' { 48 | It 'Given a account to remove from a right, calls RemoveAccountRights' { 49 | Revoke-UserRight @defaultParams 50 | 51 | Assert-MockCalled OpenLsaPolicy -Scope It -Times 1 52 | Assert-MockCalled CloseLsaPolicy -Scope It -Times 1 53 | 54 | $Script:removeWasCalled | Should -Be 1 55 | $Script:removeAllWasCalled | Should -Be 0 56 | } 57 | 58 | It 'When AllRights is set, removes the account from all assigned rights' { 59 | Revoke-UserRight -AccountName $defaultParams['AccountName'] -AllRights 60 | 61 | Assert-MockCalled OpenLsaPolicy -Scope It -Times 1 62 | Assert-MockCalled CloseLsaPolicy -Scope It -Times 1 63 | 64 | $Script:removeWasCalled | Should -Be 0 65 | $Script:removeAllWasCalled | Should -Be 1 66 | } 67 | } 68 | 69 | Context 'ShouldProcess' { 70 | It 'When WhatIf is used, does not call RemoveAccountRights' { 71 | Revoke-UserRight @defaultParams -WhatIf 72 | 73 | Assert-MockCalled OpenLsaPolicy -Scope It -Times 1 74 | Assert-MockCalled CloseLsaPolicy -Scope It -Times 1 75 | 76 | $Script:removeWasCalled | Should -Be 0 77 | } 78 | } 79 | 80 | Context 'Error handling - RemoveAccountRights' { 81 | BeforeAll { 82 | Mock OpenLsaPolicy { 83 | [PSCustomObject]@{} | 84 | Add-Member RemoveAccountRights -MemberType ScriptMethod -PassThru -Value { 85 | throw 'remove error' 86 | } 87 | } 88 | } 89 | 90 | It 'When RemoveAccountRights throws, writes an error' { 91 | { Revoke-UserRight @defaultParams -ErrorAction Stop } | Should -Throw 'remove error' 92 | { Revoke-UserRight @defaultParams -ErrorAction SilentlyContinue } | Should -Not -Throw 93 | 94 | Assert-MockCalled OpenLsaPolicy -Scope It -Times 2 95 | Assert-MockCalled CloseLsaPolicy -Scope It -Times 1 96 | } 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/en/about_UserRights.txt: -------------------------------------------------------------------------------- 1 | TOPIC 2 | about_UserRights 3 | 4 | SHORT DESCRIPTION 5 | Describes the User rights assignment names. 6 | 7 | LONG DESCRIPTION 8 | User rights assignments are accessed using constant names. 9 | 10 | The following constant names are defined in this module. 11 | 12 | SeTrustedCredManAccessPrivilege Access Credential Manager as a trusted caller 13 | SeNetworkLogonRight Access this computer from the network 14 | SeTcbPrivilege Act as part of the operating system 15 | SeMachineAccountPrivilege Add workstations to domain 16 | SeIncreaseQuotaPrivilege Adjust memory quotas for a process 17 | SeInteractiveLogonRight Allow log on locally 18 | SeRemoteInteractiveLogonRight Allow log on through Remote Desktop Services 19 | SeBackupPrivilege Back up files and directories 20 | SeChangeNotifyPrivilege Bypass traverse checking 21 | SeSystemtimePrivilege Change the system time 22 | SeTimeZonePrivilege Change the time zone 23 | SeCreatePagefilePrivilege Create a pagefile 24 | SeCreateTokenPrivilege Create a token object 25 | SeCreateGlobalPrivilege Create global objects 26 | SeCreatePermanentPrivilege Create permanent shared objects 27 | SeCreateSymbolicLinkPrivilege Create symbolic links 28 | SeDebugPrivilege Debug programs 29 | SeDenyNetworkLogonRight Deny access this computer from the network 30 | SeDenyBatchLogonRight Deny log on as a batch job 31 | SeDenyServiceLogonRight Deny log on as a service 32 | SeDenyInteractiveLogonRight Deny log on locally 33 | SeDenyRemoteInteractiveLogonRight Deny log on through Remote Desktop Services 34 | SeEnableDelegationPrivilege Enable computer and user accounts to be trusted for delegation 35 | SeRemoteShutdownPrivilege Force shutdown from a remote system 36 | SeAuditPrivilege Generate security audits 37 | SeImpersonatePrivilege Impersonate a client after authentication 38 | SeIncreaseWorkingSetPrivilege Increase a process working set 39 | SeIncreaseBasePriorityPrivilege Increase scheduling priority 40 | SeLoadDriverPrivilege Load and unload device drivers 41 | SeLockMemoryPrivilege Lock pages in memory 42 | SeBatchLogonRight Log on as a batch job 43 | SeServiceLogonRight Log on as a service 44 | SeSecurityPrivilege Manage auditing and security log 45 | SeRelabelPrivilege Modify an object label 46 | SeSystemEnvironmentPrivilege Modify firmware environment values 47 | SeManageVolumePrivilege Perform volume maintenance tasks 48 | SeProfileSingleProcessPrivilege Profile single process 49 | SeSystemProfilePrivilege Profile system performance 50 | SeUnsolicitedInputPrivilege Read unsolicited input from a terminal device 51 | SeUndockPrivilege Remove computer from docking station 52 | SeAssignPrimaryTokenPrivilege Replace a process level token 53 | SeRestorePrivilege Restore files and directories 54 | SeShutdownPrivilege Shut down the system 55 | SeSyncAgentPrivilege Synchronize directory service data 56 | SeTakeOwnershipPrivilege Take ownership of files or other objects 57 | 58 | 59 | SEE ALSO 60 | https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/user-rights-assignment -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/SecurityPolicy/UserRight.cs: -------------------------------------------------------------------------------- 1 | namespace Indented.SecurityPolicy 2 | { 3 | public enum UserRight : int 4 | { 5 | SeTrustedCredManAccessPrivilege, // Access Credential Manager as a trusted caller 6 | SeNetworkLogonRight, // Access this computer from the network 7 | SeTcbPrivilege, // Act as part of the operating system 8 | SeMachineAccountPrivilege, // Add workstations to domain 9 | SeIncreaseQuotaPrivilege, // Adjust memory quotas for a process 10 | SeInteractiveLogonRight, // Allow log on locally 11 | SeRemoteInteractiveLogonRight, // Allow log on through Remote Desktop Services 12 | SeBackupPrivilege, // Back up files and directories 13 | SeChangeNotifyPrivilege, // Bypass traverse checking 14 | SeSystemtimePrivilege, // Change the system time 15 | SeTimeZonePrivilege, // Change the time zone 16 | SeCreatePagefilePrivilege, // Create a pagefile 17 | SeCreateTokenPrivilege, // Create a token object 18 | SeCreateGlobalPrivilege, // Create global objects 19 | SeCreatePermanentPrivilege, // Create permanent shared objects 20 | SeCreateSymbolicLinkPrivilege, // Create symbolic links 21 | SeDebugPrivilege, // Debug programs 22 | SeDenyNetworkLogonRight, // Deny access this computer from the network 23 | SeDenyBatchLogonRight, // Deny log on as a batch job 24 | SeDenyServiceLogonRight, // Deny log on as a service 25 | SeDenyInteractiveLogonRight, // Deny log on locally 26 | SeDenyRemoteInteractiveLogonRight, // Deny log on through Remote Desktop Services 27 | SeEnableDelegationPrivilege, // Enable computer and user accounts to be trusted for delegation 28 | SeRemoteShutdownPrivilege, // Force shutdown from a remote system 29 | SeAuditPrivilege, // Generate security audits 30 | SeImpersonatePrivilege, // Impersonate a client after authentication 31 | SeIncreaseWorkingSetPrivilege, // Increase a process working set 32 | SeIncreaseBasePriorityPrivilege, // Increase scheduling priority 33 | SeLoadDriverPrivilege, // Load and unload device drivers 34 | SeLockMemoryPrivilege, // Lock pages in memory 35 | SeBatchLogonRight, // Log on as a batch job 36 | SeServiceLogonRight, // Log on as a service 37 | SeSecurityPrivilege, // Manage auditing and security log 38 | SeRelabelPrivilege, // Modify an object label 39 | SeSystemEnvironmentPrivilege, // Modify firmware environment values 40 | SeManageVolumePrivilege, // Perform volume maintenance tasks 41 | SeProfileSingleProcessPrivilege, // Profile single process 42 | SeSystemProfilePrivilege, // Profile system performance 43 | SeUndockPrivilege, // Remove computer from docking station 44 | SeAssignPrimaryTokenPrivilege, // Replace a process level token 45 | SeRestorePrivilege, // Restore files and directories 46 | SeShutdownPrivilege, // Shut down the system 47 | SeSyncAgentPrivilege, // Synchronize directory service data 48 | SeTakeOwnershipPrivilege, // Take ownership of files or other objects 49 | SeDelegateSessionUserImpersonatePrivilege // Obtain an impersonation token for another user in the same session 50 | } 51 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/public/Get-SecurityOption.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe Get-SecurityOption { 19 | BeforeAll { 20 | Mock Get-Item { 21 | [PSCustomObject]@{ 22 | PSPath = 'TestDrive:\Software\Microsoft' 23 | } | Add-Member GetValueNames -MemberType ScriptMethod -PassThru -Value { 24 | return 'EnableLUA' 25 | } 26 | } 27 | Mock Get-ItemPropertyValue { 28 | 0 29 | } 30 | Mock Test-Path { $true } 31 | 32 | Mock NewImplementingType { 33 | [PSCustomObject]@{ 34 | Value = $null 35 | } | Add-Member Get -MemberType ScriptMethod -PassThru -Value { 36 | $this.Value = 'Disabled' 37 | 38 | return $this 39 | } 40 | } 41 | 42 | $defaultParams = @{ 43 | Name = 'EnableLUA' 44 | } 45 | } 46 | 47 | Context 'Registry value, key exists, value exists' { 48 | It 'Gets the value and converts it to an enumerated value' { 49 | $securityOption = Get-SecurityOption @defaultParams 50 | 51 | $securityOption.Name | Should -Be 'EnableLUA' 52 | $securityOption.Value | Should -Be 'Disabled' 53 | } 54 | } 55 | 56 | Context 'Registry value, key exists, value does not exist' { 57 | Mock Get-Item { 58 | [PSCustomObject]@{ 59 | PSPath = 'TestDrive:\Software\Microsoft' 60 | } | Add-Member GetValueNames -MemberType ScriptMethod -PassThru -Value {} 61 | } 62 | 63 | It 'Returns the default value for the policy' { 64 | $securityOption = Get-SecurityOption @defaultParams 65 | 66 | $securityOption.Name | Should -Be 'EnableLUA' 67 | $securityOption.Value | Should -Be 'Enabled' 68 | } 69 | } 70 | 71 | Context 'Registry value, key does not exist' { 72 | Mock Test-Path { $false } 73 | 74 | It 'Returns the default value for the policy' { 75 | $securityOption = Get-SecurityOption @defaultParams 76 | 77 | $securityOption.Name | Should -Be 'EnableLUA' 78 | $securityOption.Value | Should -Be 'Enabled' 79 | } 80 | } 81 | 82 | Context 'Class value' { 83 | BeforeAll { 84 | $contextParams = @{ 85 | Name = 'GuestAccountStatus' 86 | } 87 | } 88 | 89 | It 'Calls Get and returns the value' { 90 | $securityOption = Get-SecurityOption @contextParams 91 | 92 | $securityOption.Name | Should -Be 'GuestAccountStatus' 93 | $securityOption.Value | Should -Be 'Disabled' 94 | } 95 | } 96 | 97 | Context 'Error handling' { 98 | Mock Get-Item { 99 | throw 'Access denied' 100 | } 101 | 102 | It 'When an error is thrown when acquiring a value, throws a non-terminating error' { 103 | { Get-SecurityOption @defaultParams -ErrorAction Stop } | Should -Throw -ErrorId 'FailedToRetrieveSecurityOptionSetting' 104 | { Get-SecurityOption @defaultParams -ErrorAction SilentlyContinue } | Should -Not -Throw 105 | } 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/private/ImportSecurityOptionData.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe ImportSecurityOptionData { 19 | AfterAll { 20 | ImportSecurityOptionData 21 | } 22 | 23 | Context 'Import' { 24 | BeforeAll { 25 | Mock Import-LocalizedData { 26 | Set-Variable $BindingVariable -Scope Script -Value @{ 27 | EnableLUA = 'User Account Control: Run all administrators in Admin Approval Mode' 28 | PromptOnSecureDesktop = 'User Account Control: Switch to the secure desktop when prompting for elevation' 29 | } 30 | } 31 | Mock Import-PowerShellDataFile { 32 | @{ 33 | EnableLUA = @{ 34 | Key = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System' 35 | ValueType = 'Enabled' 36 | Default = 'Enabled' 37 | } 38 | PromptOnSecureDesktop = @{ 39 | Key = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System' 40 | ValueType = 'Enabled' 41 | Default = 'Enabled' 42 | } 43 | } 44 | } 45 | } 46 | 47 | BeforeEach { 48 | $Script:securityOptionData = $null 49 | $Script:securityOptionLookupHelper = $null 50 | } 51 | 52 | It 'Fills a module scoped hashtable with values' { 53 | ImportSecurityOptionData 54 | 55 | $Script:securityOptionData | Should -Not -BeNullOrEmpty 56 | $Script:securityOptionLookupHelper | Should -Not -BeNullOrEmpty 57 | } 58 | 59 | It 'Converts the description "" to the name ""' -TestCases @( 60 | @{ Description = 'User Account Control: Run all administrators in Admin Approval Mode'; Name = 'EnableLUA' } 61 | @{ Description = 'User Account Control: Switch to the secure desktop when prompting for elevation'; Name = 'PromptOnSecureDesktop' } 62 | ) { 63 | param ( $Description, $Name ) 64 | 65 | ImportSecurityOptionData 66 | 67 | $Script:securityOptionLookupHelper.Contains($Description) | Should -BeTrue 68 | $Script:securityOptionLookupHelper[$Description] | Should -Be $Name 69 | } 70 | 71 | It 'Handles culture fallback' -TestCases @( 72 | @{ Culture = 'en-US' } 73 | @{ Culture = 'en-GB' } 74 | @{ Culture = 'da-DK' } 75 | @{ Culture = 'da' } 76 | @{ Culture = 'sv-FI' } 77 | ) { 78 | param ( 79 | $Culture 80 | ) 81 | 82 | $currentCulture = $PSUICulture 83 | 84 | & { 85 | [System.Threading.Thread]::CurrentThread.CurrentUICulture = $Culture 86 | 87 | ImportSecurityOptionData 88 | 89 | $Description = 'User Account Control: Run all administrators in Admin Approval Mode' 90 | $Name = 'EnableLUA' 91 | 92 | $Script:securityOptionLookupHelper[$Description] | Should -Be $Name 93 | 94 | [System.Threading.Thread]::CurrentThread.CurrentUICulture = $currentCulture 95 | } 96 | } 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/Dsc/RegistryPolicy.ps1: -------------------------------------------------------------------------------- 1 | [DscResource()] 2 | class RegistryPolicy { 3 | [DscProperty()] 4 | [Ensure]$Ensure = 'Present' 5 | 6 | [DscProperty(Key)] 7 | [String]$Name 8 | 9 | [DscProperty(Key)] 10 | [String]$Path 11 | 12 | [DscProperty()] 13 | [String[]]$Data = @() 14 | 15 | [DscProperty()] 16 | [RegistryValueType]$ValueType = 'String' 17 | 18 | Hidden [Object] $ParsedData 19 | 20 | Hidden [Boolean] CompareValue() { 21 | $value = Get-ItemPropertyValue -Path $this.Path -Name $this.Name 22 | if ($this.ValueType -eq 'MultiString') { 23 | if (Compare-Object @($this.ParsedData) @($value) -SyncWindow 0) { 24 | return $false 25 | } 26 | } elseif ($value -ne $this.ParsedData) { 27 | return $false 28 | } 29 | 30 | return $true 31 | } 32 | 33 | Hidden [Void] ParseData() { 34 | try { 35 | $this.ParsedData = switch ($this.ValueType) { 36 | 'DWord' { [UInt32]::Parse($this.Data[0]); break } 37 | 'QWord' { [UInt64]::Parse($this.Data[0]); break } 38 | 'MultiString' { $this.Data; break } 39 | 'Binary' { 40 | foreach ($value in $this.Data -split ' ') { 41 | [Convert]::ToByte($value, 16) 42 | } 43 | break 44 | } 45 | default { $this.Data[0] } 46 | } 47 | } catch { 48 | throw 49 | } 50 | } 51 | 52 | [RegistryPolicy] Get() { 53 | $this.ParseData() 54 | 55 | if (-not $this.Test()) { 56 | $this.Ensure = $this.Ensure -bxor 1 57 | } 58 | 59 | return $this 60 | } 61 | 62 | [Void] Set() { 63 | $this.ParseData() 64 | 65 | $params = @{ 66 | Name = $this.Name 67 | Path = $this.Path 68 | } 69 | 70 | if ($this.Ensure -eq 'Present') { 71 | if (Test-Path $this.Path) { 72 | $key = Get-Item $this.Path 73 | } else { 74 | $key = New-Item $this.Path -ItemType Key -Force 75 | } 76 | 77 | if ($this.Name -in $key.GetValueNames()) { 78 | if ($key.GetValueKind($this.Name).ToString() -eq $this.ValueType) { 79 | if (-not $this.CompareValue()) { 80 | Set-ItemProperty -Value $this.ParsedData @params 81 | } 82 | } else { 83 | Remove-ItemProperty @params 84 | New-ItemProperty -PropertyType $this.ValueType -Value $this.ParsedData @params 85 | } 86 | } else { 87 | New-ItemProperty -PropertyType $this.ValueType -Value $this.ParsedData @params 88 | } 89 | } elseif ($this.Ensure -eq 'Absent') { 90 | if (Test-Path $this.Path) { 91 | $key = Get-Item $this.Path 92 | if ($this.Name -in $key.GetValueNames()) { 93 | Remove-ItemProperty @params 94 | } 95 | } 96 | } 97 | } 98 | 99 | [Boolean] Test() { 100 | $this.ParseData() 101 | 102 | if ($this.Ensure -eq 'Present') { 103 | if (-not (Test-Path $this.Path)) { 104 | return $false 105 | } 106 | 107 | $key = Get-Item $this.Path 108 | if ($key.GetValueNames() -notcontains $this.Name) { 109 | return $false 110 | } 111 | 112 | if ($key.GetValueKind($this.Name).ToString() -ne $this.ValueType) { 113 | return $false 114 | } 115 | 116 | return $this.CompareValue() 117 | } elseif ($this.Ensure -eq 'Absent') { 118 | if (Test-Path $this.Path) { 119 | $key = Get-Item $this.Path 120 | if ($key.GetValueNames() -contains $this.Name) { 121 | return $false 122 | } 123 | } 124 | } 125 | 126 | return $true 127 | } 128 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/Indented.SecurityPolicy.format.ps1xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Indented.SecurityPolicy.SecurityOptionInfo 6 | 7 | Indented.SecurityPolicy.SecurityOptionInfo 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Name 19 | 20 | 21 | Description 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Indented.SecurityPolicy.SecurityOptionSetting 30 | 31 | Indented.SecurityPolicy.SecurityOptionSetting 32 | 33 | 34 | Category 35 | 36 | 37 | 38 | 39 | 30 40 | 41 | 42 | 43 | 60 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | Name 52 | 53 | 54 | ShortDescription 55 | 56 | 57 | Value 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | Indented.SecurityPolicy.UserRightInfo 66 | 67 | Indented.SecurityPolicy.UserRightInfo 68 | 69 | 70 | 71 | 72 | left 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | Name 81 | 82 | 83 | Description 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | Indented.SecurityPolicy.UserRightSetting 92 | 93 | Indented.SecurityPolicy.UserRightSetting 94 | 95 | 96 | 97 | 98 | left 99 | 100 | 101 | 30 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | Name 110 | 111 | 112 | Description 113 | 114 | 115 | AccountName 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/Indented.SecurityPolicy.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'Indented.SecurityPolicy' 3 | # 4 | # Generated by: Chris Dent 5 | # 6 | # Generated on: 29/11/2018 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'Indented.SecurityPolicy.psm1' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '1.3.2' 16 | 17 | # Supported PSEditions 18 | CompatiblePSEditions = @('Core', 'Desktop') 19 | 20 | # ID used to uniquely identify this module 21 | GUID = 'e1d90894-388a-42a1-aa09-b7bb0bbdfae0' 22 | 23 | # Author of this module 24 | Author = 'Chris Dent' 25 | 26 | # Company or vendor of this module 27 | CompanyName = 'Chris Dent' 28 | 29 | # Copyright statement for this module 30 | Copyright = '2018 Chris Dent' 31 | 32 | # Description of the functionality provided by this module 33 | Description = 'Security management functions and resources' 34 | 35 | # Minimum version of the Windows PowerShell engine required by this module 36 | PowerShellVersion = '5.1' 37 | 38 | # Name of the Windows PowerShell host required by this module 39 | # PowerShellHostName = '' 40 | 41 | # Minimum version of the Windows PowerShell host required by this module 42 | # PowerShellHostVersion = '' 43 | 44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 45 | # DotNetFrameworkVersion = '' 46 | 47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 48 | # CLRVersion = '' 49 | 50 | # Processor architecture (None, X86, Amd64) required by this module 51 | # ProcessorArchitecture = '' 52 | 53 | # Modules that must be imported into the global environment prior to importing this module 54 | # RequiredModules = @() 55 | 56 | # Assemblies that must be loaded prior to importing this module 57 | # RequiredAssemblies = @() 58 | 59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 60 | # ScriptsToProcess = @() 61 | 62 | # Type files (.ps1xml) to be loaded when importing this module 63 | # TypesToProcess = @() 64 | 65 | # Format files (.ps1xml) to be loaded when importing this module 66 | # FormatsToProcess = @() 67 | 68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 69 | # NestedModules = @() 70 | 71 | # 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. 72 | FunctionsToExport = @() 73 | 74 | # 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. 75 | CmdletsToExport = @() 76 | 77 | # Variables to export from this module 78 | VariablesToExport = '*' 79 | 80 | # 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. 81 | AliasesToExport = @() 82 | 83 | # DSC resources to export from this module 84 | # DscResourcesToExport = @() 85 | 86 | # List of all modules packaged with this module 87 | # ModuleList = @() 88 | 89 | # List of all files packaged with this module 90 | # FileList = @() 91 | 92 | # 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. 93 | PrivateData = @{ 94 | 95 | PSData = @{ 96 | 97 | # Tags applied to this module. These help with module discovery in online galleries. 98 | # Tags = @() 99 | 100 | # A URL to the license for this module. 101 | # LicenseUri = '' 102 | 103 | # A URL to the main website for this project. 104 | # ProjectUri = '' 105 | 106 | # A URL to an icon representing this module. 107 | # IconUri = '' 108 | 109 | # ReleaseNotes of this module 110 | # ReleaseNotes = '' 111 | 112 | } # End of PSData hashtable 113 | 114 | } # End of PrivateData hashtable 115 | 116 | # HelpInfo URI of this module 117 | # HelpInfoURI = '' 118 | 119 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 120 | # DefaultCommandPrefix = '' 121 | 122 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/dsc/GroupManagedServiceAccount.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe GroupManagedServiceAccount { 19 | BeforeAll { 20 | Mock Install-GroupManagedServiceAccount 21 | Mock Test-GroupManagedServiceAccount { 22 | return $true 23 | } 24 | Mock Uninstall-GroupManagedServiceAccount 25 | } 26 | 27 | BeforeEach { 28 | $class = [GroupManagedServiceAccount]::new() 29 | $class.Ensure = 'Present' 30 | $class.Name = 'accountName$' 31 | } 32 | 33 | Context 'Get, account is present' { 34 | It 'When the account is present, sets Ensure to present' { 35 | $class.Ensure = 'Absent' 36 | 37 | $instance = $class.Get() 38 | $instance.Ensure | Should -Be 'Present' 39 | } 40 | } 41 | 42 | Context 'Get, account is absent' { 43 | BeforeAll { 44 | Mock Test-GroupManagedServiceAccount { 45 | return $false 46 | } 47 | } 48 | 49 | It 'When the account is absent, sets Ensure to absent' { 50 | $class.Ensure = 'Present' 51 | 52 | $instance = $class.Get() 53 | $instance.Ensure | Should -Be 'Absent' 54 | } 55 | } 56 | 57 | Context 'Set, account is present' { 58 | It 'When ensure is present, does nothing' { 59 | $class.Ensure = 'Present' 60 | $class.Set() 61 | 62 | Assert-MockCalled Install-GroupManagedServiceAccount -Times 0 -Scope It 63 | Assert-MockCalled Uninstall-GroupManagedServiceAccount -Times 0 -Scope It 64 | } 65 | 66 | It 'When ensure is absent, uninstalls the account' { 67 | $class.Ensure = 'Absent' 68 | $class.Set() 69 | 70 | Assert-MockCalled Install-GroupManagedServiceAccount -Times 0 -Scope It 71 | Assert-MockCalled Uninstall-GroupManagedServiceAccount -Times 1 -Scope It 72 | } 73 | } 74 | 75 | Context 'Set, account is absent' { 76 | BeforeAll { 77 | Mock Test-GroupManagedServiceAccount { 78 | return $false 79 | } 80 | } 81 | 82 | It 'When ensure is present, installs the account' { 83 | $class.Ensure = 'Present' 84 | $class.Set() 85 | 86 | Assert-MockCalled Install-GroupManagedServiceAccount -Times 1 -Scope It 87 | Assert-MockCalled Uninstall-GroupManagedServiceAccount -Times 0 -Scope It 88 | } 89 | 90 | It 'When ensure is absent, does nothing' { 91 | $class.Ensure = 'Absent' 92 | $class.Set() 93 | 94 | Assert-MockCalled Install-GroupManagedServiceAccount -Times 0 -Scope It 95 | Assert-MockCalled Uninstall-GroupManagedServiceAccount -Times 0 -Scope It 96 | } 97 | } 98 | 99 | Context 'Test, account is present' { 100 | It 'When ensure is present, returns true' { 101 | $class.Ensure = 'Present' 102 | 103 | $class.Test() | Should -Be $true 104 | } 105 | 106 | It 'When ensure is absent, returns false' { 107 | $class.Ensure = 'Absent' 108 | 109 | $class.Test() | Should -Be $false 110 | } 111 | } 112 | 113 | Context 'Test, account is absent' { 114 | BeforeAll { 115 | Mock Test-GroupManagedServiceAccount { 116 | return $false 117 | } 118 | } 119 | 120 | It 'When ensure is present, returns false' { 121 | $class.Ensure = 'Present' 122 | 123 | $class.Test() | Should -Be $false 124 | } 125 | 126 | It 'When ensure is absent, returns true' { 127 | $class.Ensure = 'Absent' 128 | 129 | $class.Test() | Should -Be $true 130 | } 131 | } 132 | } 133 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/public/Clear-UserRight.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe Clear-UserRight { 19 | BeforeAll { 20 | Mock CloseLsaPolicy 21 | Mock OpenLsaPolicy { 22 | [PSCustomObject]@{} | 23 | Add-Member EnumerateAccountsWithUserRight -MemberType ScriptMethod -PassThru -Value { 24 | $Script:accountNames 25 | } | 26 | Add-Member RemoveAccountRights -MemberType ScriptMethod -PassThru -Value { 27 | $Script:removeWasCalled++ 28 | } 29 | } 30 | Mock Resolve-UserRight { 31 | [PSCustomObject]@{ 32 | Name = 'SeDenyServiceLogonRight' 33 | } 34 | } 35 | 36 | $defaultParams = @{ 37 | Name = 'SeDenyServiceLogonRight' 38 | } 39 | } 40 | 41 | BeforeEach { 42 | $Script:removeWasCalled = 0 43 | $Script:accountNames = 'first', 'second' 44 | } 45 | 46 | Context 'Accounts are assigned the right' { 47 | It 'When a policy has accounts defined, removes all accounts' { 48 | Clear-UserRight @defaultParams 49 | 50 | Assert-MockCalled OpenLsaPolicy -Scope It -Times 1 51 | Assert-MockCalled CloseLsaPolicy -Scope It -Times 1 52 | 53 | $Script:removeWasCalled | Should -Be 2 54 | } 55 | } 56 | 57 | Context 'No accounts are assigned the right' { 58 | BeforeEach { 59 | $Script:accountNames = $null 60 | } 61 | 62 | It 'When a policy does not have accounts assigned, does nothing' { 63 | Clear-UserRight @defaultParams 64 | 65 | Assert-MockCalled OpenLsaPolicy -Scope It -Times 1 66 | Assert-MockCalled CloseLsaPolicy -Scope It -Times 1 67 | 68 | $Script:removeWasCalled | Should -Be 0 69 | } 70 | } 71 | 72 | Context 'Error handling - EnumerateAccountsWithUserRight' { 73 | BeforeAll { 74 | Mock OpenLsaPolicy { 75 | [PSCustomObject]@{} | 76 | Add-Member EnumerateAccountsWithUserRight -MemberType ScriptMethod -PassThru -Value { 77 | throw 'enumerate error' 78 | } | 79 | Add-Member RemoveAccountRights -MemberType ScriptMethod -PassThru -Value { 80 | $Script:removeWasCalled++ 81 | } 82 | } 83 | } 84 | 85 | It 'When EnumerateAccountsWithUserRight throws, writes an error' { 86 | { Clear-UserRight @defaultParams -ErrorAction Stop } | Should -Throw 'enumerate error' 87 | { Clear-UserRight @defaultParams -ErrorAction SilentlyContinue } | Should -Not -Throw 88 | 89 | Assert-MockCalled OpenLsaPolicy -Scope It -Times 2 90 | Assert-MockCalled CloseLsaPolicy -Scope It -Times 1 91 | } 92 | } 93 | 94 | Context 'Error handling - RemoveAccountRights' { 95 | BeforeAll { 96 | Mock OpenLsaPolicy { 97 | [PSCustomObject]@{} | 98 | Add-Member EnumerateAccountsWithUserRight -MemberType ScriptMethod -PassThru -Value { 99 | $Script:accountNames 100 | } | 101 | Add-Member RemoveAccountRights -MemberType ScriptMethod -PassThru -Value { 102 | throw 'remove error' 103 | } 104 | } 105 | } 106 | 107 | It 'When RemoveAccountRights throws, writes an error' { 108 | { Clear-UserRight @defaultParams -ErrorAction Stop } | Should -Throw 'remove error' 109 | { Clear-UserRight @defaultParams -ErrorAction SilentlyContinue } | Should -Not -Throw 110 | 111 | Assert-MockCalled OpenLsaPolicy -Scope It -Times 2 112 | Assert-MockCalled CloseLsaPolicy -Scope It -Times 1 113 | } 114 | } 115 | } 116 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [![Build status](https://ci.appveyor.com/api/projects/status/7xs9k5m69dahdb51?svg=true)](https://ci.appveyor.com/project/indented-automation/indented-securitypolicy) 2 | 3 | # Indented.SecurityPolicy 4 | 5 | This module provides commands and DSC resources for manipulating and maintaining User Rights Assignment, Security Options, and Group Managed Service Account installation. 6 | 7 | ## Installation 8 | 9 | ```powershell 10 | Install-Module Indented.SecurityPolicy 11 | ``` 12 | 13 | ## Commands 14 | 15 | The commands below are exported by this module. 16 | 17 | ### User rights 18 | 19 | - [Clear-UserRight](Indented.SecurityPolicy/help/Clear-UserRight.md) 20 | - [Get-AssignedUserRight](Indented.SecurityPolicy/help/Get-AssignedUserRight.md) 21 | - [Get-UserRight](Indented.SecurityPolicy/help/Get-UserRight.md) 22 | - [Grant-UserRight](Indented.SecurityPolicy/help/Grant-UserRight.md) 23 | - [Grant-UserRight](Indented.SecurityPolicy/help/Grant-UserRight.md) 24 | - [Resolve-UserRight](Indented.SecurityPolicy/help/Resolve-UserRight.md) 25 | - [Revoke-UserRight](Indented.SecurityPolicy/help/Revoke-UserRight.md) 26 | - [Set-UserRight](Indented.SecurityPolicy/help/Set-UserRight.md) 27 | 28 | ### Security Option 29 | 30 | - [Get-SecurityOption](Indented.SecurityPolicy/help/Get-SecurityOption.md) 31 | - [Reset-SecurityOption](Indented.SecurityPolicy/help/Reset-SecurityOption.md) 32 | - [Resolve-SecurityOption](Indented.SecurityPolicy/help/Resolve-SecurityOption.md) 33 | - [Set-SecurityOption](Indented.SecurityPolicy/help/Set-SecurityOption.md) 34 | 35 | ### Service accounts 36 | 37 | - [Install-GroupManagedServiceAccount](Indented.SecurityPolicy/help/Install-GroupManagedServiceAccount.md) 38 | - [Test-GroupManagedServiceAccount](Indented.SecurityPolicy/help/Test-GroupManagedServiceAccount.md) 39 | - [Uninstall-GroupManagedServiceAccount](Indented.SecurityPolicy/help/Uninstall-GroupManagedServiceAccount.md) 40 | 41 | ## DSC resources 42 | 43 | The following DSC resources are made available. 44 | 45 | ### GroupManagedServiceAccount 46 | 47 | - **Ensure** - _Optional_. Present by default. 48 | - **Name** - _Mandatory_. The SamAccountName of the account to install. 49 | 50 | Example usage: 51 | 52 | ```powershell 53 | GroupManagedServiceAccount AccountName { 54 | Ensure = 'Present' 55 | Name = 'Username$' 56 | } 57 | ``` 58 | 59 | ### RegistryPolicy 60 | 61 | - **Ensure** - _Optional_. Present by default. 62 | - **Name** - _Key_. The name of the policy, a registry value. 63 | - **Path** - _Key_. The path to the registry key. 64 | - **Data** - _Optional_. Should be defined if Ensure is present. 65 | - **ValueType** - _Optional_. String by default. Permissible values: String, DWord, QWord, MultiString, and Binary. 66 | 67 | A helper resource used to configure arbitrary policies. 68 | 69 | ```powershell 70 | RegistryPolicy LocalAccountTokenFilterPolicy { 71 | Ensure = 'Present' 72 | Name = 'LocalAccountTokenFilterPolicy' 73 | Path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' 74 | Data = 0 75 | ValueType = 'DWord' 76 | } 77 | ``` 78 | 79 | ### SecurityOption 80 | 81 | - **Ensure** - _Optional_. Present by default. 82 | - **Name** - _Key_. The name or descriptive name of the policy. See Resolve-SecurityOption. 83 | - **Value** - _Optional_. Should be defined if Ensure is present. A value consistent with the value type for the option. 84 | 85 | Policies may be referenced either using the short name, see Resolve-SecurityOption, or the long policy name. 86 | 87 | Example usage: 88 | 89 | ```powershell 90 | SecurityOption EnableLUA { 91 | Ensure = 'Present' 92 | Name = 'EnableLUA' 93 | Value = 'Enabled' 94 | } 95 | 96 | SecurityOption ShutdownWithoutLogon { 97 | Ensure = 'Present' 98 | Name = 'Shutdown: Allow system to be shut down without having to log on' 99 | Value = 'Enabled' 100 | } 101 | ``` 102 | 103 | ### UserRightAssignment 104 | 105 | - **Ensure** - _Optional_. Present by default. 106 | - **Name** - _Mandatory_. The name or descriptive name of a policy. 107 | - **AccountName** - An array of accounts to add or remove. To clear the right, set Ensure to absent, and leave this list empty. 108 | - **Replace** - By default principals are added to, or removed from, the list. Setting replace to true rewrites the list. 109 | - **Description** - _NotConfigurable_ Set by the resource to the descriptive name of the policy. 110 | 111 | Rights may be referenced either using the short name, see Resolve-UserRight, or the long right name. 112 | 113 | Example usage: 114 | 115 | ```powershell 116 | UserRightAssignment SeMachineAccountPrivilege { 117 | Ensure = 'Present' 118 | Name = 'Add workstations to domain' 119 | AccountName = 'Account1', 'Account2' 120 | } 121 | ``` 122 | -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/public/Reset-SecurityOption.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe Reset-SecurityOption { 19 | BeforeAll { 20 | Mock Remove-ItemProperty 21 | Mock Get-Item { 22 | [PSCustomObject]@{ 23 | PSPath = 'TestDrive:\Software\Microsoft' 24 | } | Add-Member GetValueNames -MemberType ScriptMethod -PassThru -Value { 25 | return @('MachineAccessRestriction') 26 | } 27 | } 28 | Mock Set-SecurityOption 29 | Mock Test-Path { $true } 30 | 31 | $defaultParams = @{ 32 | Name = 'MachineAccessRestriction' 33 | } 34 | } 35 | 36 | Context 'Registry value, "Not Defined", key exists, value exists' { 37 | BeforeAll { 38 | $contextParams = @{ 39 | Name = 'MachineAccessRestriction' 40 | } 41 | } 42 | 43 | It 'When the default value is "Not Defined", removes the registry value' { 44 | Reset-SecurityOption @contextParams 45 | 46 | Assert-MockCalled Remove-ItemProperty -Times 1 -Scope It 47 | Assert-MockCalled Set-SecurityOption -Times 0 -Scope It 48 | } 49 | } 50 | 51 | Context 'Registry value, "Not Defined", key does not exist' { 52 | BeforeAll { 53 | Mock Test-Path { $false } 54 | } 55 | 56 | It 'When the key does not exist, does nothing' { 57 | Reset-SecurityOption @defaultParams 58 | 59 | Assert-MockCalled Remove-ItemProperty -Times 0 60 | Assert-MockCalled Set-SecurityOption -Times 0 61 | } 62 | } 63 | 64 | Context 'Registry value, "Not Defined", value does not exist' { 65 | BeforeAll { 66 | Mock Get-Item { 67 | [PSCustomObject]@{ 68 | PSPath = 'TestDrive:\Software\Microsoft' 69 | } | Add-Member GetValueNames -MemberType ScriptMethod -PassThru -Value {} 70 | } 71 | } 72 | 73 | It 'When the value does not exist, does nothing' { 74 | Reset-SecurityOption @defaultParams 75 | 76 | Assert-MockCalled Remove-ItemProperty -Times 0 -Scope It 77 | Assert-MockCalled Set-SecurityOption -Times 0 -Scope It 78 | } 79 | } 80 | 81 | Context 'All values other than "Not Defined"' { 82 | It 'When a class-based change is requested, calls Set-SecurityOption' { 83 | $testParams = @{ 84 | Name = 'GuestAccountStatus' 85 | } 86 | 87 | Reset-SecurityOption @testParams 88 | 89 | Assert-MockCalled Remove-ItemProperty -Times 0 -Scope It 90 | Assert-MockCalled Set-SecurityOption -Times 1 -Scope It 91 | } 92 | 93 | It 'When a registry change is requested, and the default value is something other than "Not Defined"' { 94 | $testParams = @{ 95 | Name = 'LimitBlankPasswordUse' 96 | } 97 | 98 | Reset-SecurityOption @testParams 99 | 100 | Assert-MockCalled Remove-ItemProperty -Times 0 -Scope It 101 | Assert-MockCalled Set-SecurityOption -Times 1 -Scope It 102 | } 103 | 104 | It 'When the default value is an empty string, passes the empty string to Set-SecurityOption' { 105 | $testParams = @{ 106 | Name = 'LegalNoticeText' 107 | } 108 | 109 | Reset-SecurityOption @testParams 110 | 111 | Assert-MockCalled Remove-ItemProperty -Times 0 -Scope It 112 | Assert-MockCalled Set-SecurityOption -Times 1 -Scope It 113 | } 114 | 115 | It 'When the default value is an empty array, passes the empty array to Set-SecurityOption' { 116 | $testParams = @{ 117 | Name = 'NullSessionShares' 118 | } 119 | 120 | Reset-SecurityOption @testParams 121 | 122 | Assert-MockCalled Remove-ItemProperty -Times 0 -Scope It 123 | Assert-MockCalled Set-SecurityOption -Times 1 -Scope It 124 | } 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/public/Set-UserRight.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe Set-UserRight { 19 | 20 | BeforeAll { 21 | Mock CloseLsaPolicy 22 | Mock OpenLsaPolicy { 23 | [PSCustomObject]@{} | 24 | Add-Member EnumerateAccountsWithUserRight -MemberType ScriptMethod -PassThru -Value { 25 | $Script:accountNames 26 | } | 27 | Add-Member AddAccountRights -MemberType ScriptMethod -PassThru -Value { 28 | $Script:addWasCalled++ 29 | } | 30 | Add-Member RemoveAccountRights -MemberType ScriptMethod -PassThru -Value { 31 | $Script:removeWasCalled++ 32 | } 33 | } 34 | Mock Resolve-UserRight { 35 | [PSCustomObject]@{ 36 | Name = 'SeDenyServiceLogonRight' 37 | } 38 | } 39 | 40 | $defaultParams = @{ 41 | AccountName = 'Administrators', 'NT AUTHORITY\SYSTEM' 42 | Name = 'SeDenyServiceLogonRight' 43 | } 44 | } 45 | 46 | BeforeEach { 47 | $Script:addWasCalled = 0 48 | $Script:removeWasCalled = 0 49 | 50 | $Script:accountNames = @( 51 | [System.Security.Principal.NTAccount]'Administrators', 52 | [System.Security.Principal.NTAccount]'NT AUTHORITY\SYSTEM' 53 | ) 54 | } 55 | 56 | Context 'Account list matches' { 57 | It 'When no changes are required, does not call AddAccountRights or RemoveAccountRights' { 58 | Set-UserRight @defaultParams 59 | 60 | $Script:addWasCalled | Should -Be 0 61 | $Script:removeWasCalled | Should -Be 0 62 | } 63 | } 64 | 65 | Context 'Account list does not match' { 66 | BeforeEach { 67 | $Script:accountNames = [System.Security.Principal.NTAccount]'NT AUTHORITY\NETWORK SERVICE' 68 | } 69 | 70 | It 'When changes are required, adds and removes accounts' { 71 | Set-UserRight @defaultParams 72 | 73 | $Script:addWasCalled | Should -Be 2 74 | $Script:removeWasCalled | Should -Be 1 75 | } 76 | } 77 | 78 | Context 'Account list partial match' { 79 | BeforeEach { 80 | $Script:accountNames = @( 81 | [System.Security.Principal.NTAccount]'NT AUTHORITY\NETWORK SERVICE', 82 | [System.Security.Principal.NTAccount]'Administrators' 83 | ) 84 | } 85 | 86 | It 'When changes are required, adds and removes accounts, but does not remove matching accounts' { 87 | Set-UserRight @defaultParams 88 | 89 | $Script:addWasCalled | Should -Be 1 90 | $Script:removeWasCalled | Should -Be 1 91 | } 92 | } 93 | 94 | Context 'Account list type handling' { 95 | BeforeEach { 96 | $Script:accountNames = [System.Security.Principal.NTAccount]'Administrators' 97 | } 98 | 99 | It 'Converts a string to a SecurityIdentifier for comparison' { 100 | $accountName = 'Administrators' 101 | 102 | Set-UserRight -AccountName $accountName -Name $defaultParams['Name'] 103 | 104 | $Script:addWasCalled | Should -Be 0 105 | $Script:removeWasCalled | Should -Be 0 106 | } 107 | 108 | It 'Converts an NTAccount to a SecurityIdentifier for comparison' { 109 | $accountName = [System.Security.Principal.NTAccount]'Administrators' 110 | 111 | Set-UserRight -AccountName $accountName -Name $defaultParams['Name'] 112 | 113 | $Script:addWasCalled | Should -Be 0 114 | $Script:removeWasCalled | Should -Be 0 115 | } 116 | 117 | It 'Accepts SecurityIdentifier values' { 118 | $accountName = ([System.Security.Principal.NTAccount]'Administrators').Translate([System.Security.Principal.SecurityIdentifier]) 119 | 120 | Set-UserRight -AccountName $accountName -Name $defaultParams['Name'] 121 | 122 | $Script:addWasCalled | Should -Be 0 123 | $Script:removeWasCalled | Should -Be 0 124 | } 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/class/Dsc/UserRightAssignment.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Collections.Generic 2 | using namespace System.Security.Principal 3 | 4 | [DscResource()] 5 | class UserRightAssignment { 6 | [DscProperty()] 7 | [Ensure]$Ensure = 'Present' 8 | 9 | [DscProperty(Key)] 10 | [String]$Name 11 | 12 | [DscProperty()] 13 | [String[]]$AccountName 14 | 15 | [DscProperty()] 16 | [Boolean]$Replace 17 | 18 | [DscProperty(NotConfigurable)] 19 | [String]$Description 20 | 21 | Hidden [SecurityIdentifier[]]$requestedIdentities 22 | 23 | Hidden [SecurityIdentifier[]]$currentIdentities 24 | 25 | Hidden [Void] InitializeRequest() { 26 | try { 27 | $userRight = Resolve-UserRight $this.Name 28 | if (@($userRight).Count -ne 1) { 29 | throw 'The requested user right is ambiguous, matched right names: {0}' -f 30 | ($userRight.UserRight -join ', ') 31 | } 32 | $this.Name = $userRight.Name 33 | $this.Description = $userRight.Description 34 | } catch { 35 | throw 36 | } 37 | 38 | if ($this.Ensure -eq 'Present' -and $this.AccountName.Count -eq 0) { 39 | throw 'Invalid request. AccountName cannot be empty when ensuring a right is present.' 40 | } 41 | if ($this.Ensure -eq 'Absent' -and $this.Replace) { 42 | throw 'Replace may only be set when ensuring a set of accounts is present.' 43 | } 44 | 45 | $this.requestedIdentities = foreach ($identity in $this.AccountName) { 46 | ([NTAccount]$identity).Translate([SecurityIdentifier]) 47 | } 48 | $this.currentIdentities = foreach ($identity in (Get-UserRight -Name $this.Name).AccountName) { 49 | if ($identity -is [NTAccount]) { 50 | $identity.Translate([SecurityIdentifier]) 51 | } else { 52 | $identity 53 | } 54 | } 55 | } 56 | 57 | Hidden [Boolean] CompareAccountNames() { 58 | return [Boolean](-not (Compare-Object @($this.requestedIdentities) @($this.currentIdentities))) 59 | } 60 | 61 | Hidden [Boolean] IsAssignedRight([String]$Identity) { 62 | return $this.currentIdentities -contains ([NTAccount]$Identity).Translate([SecurityIdentifier]) 63 | } 64 | 65 | [UserRightAssignment] Get() { 66 | try { 67 | $this.InitializeRequest() 68 | $this.AccountName = (Get-UserRight -Name $this.Name).AccountName 69 | 70 | return $this 71 | } catch { 72 | throw 73 | } 74 | } 75 | 76 | [Void] Set() { 77 | try { 78 | $this.InitializeRequest() 79 | if ($this.Ensure -eq 'Present') { 80 | if ($this.Replace) { 81 | Set-UserRight -Name $this.Name -AccountName $this.AccountName 82 | } else { 83 | foreach ($identity in $this.AccountName) { 84 | if (-not $this.IsAssignedRight($identity)) { 85 | Grant-UserRight -Name $this.Name -AccountName $identity 86 | } 87 | } 88 | } 89 | } elseif ($this.Ensure -eq 'Absent') { 90 | if ($this.AccountName.Count -eq 0 -and $this.currentIdentities.Count -gt 0) { 91 | Clear-UserRight -Name $this.Name 92 | } elseif ($this.AccountName -gt 0) { 93 | foreach ($identity in $this.AccountName) { 94 | if ($this.IsAssignedRight($identity)) { 95 | Revoke-UserRight -Name $this.Name -AccountName $identity 96 | } 97 | } 98 | } 99 | } 100 | } catch { 101 | throw 102 | } 103 | } 104 | 105 | [Boolean] Test() { 106 | try { 107 | $this.InitializeRequest() 108 | $userRight = Get-UserRight -Name $this.Name 109 | 110 | if ($this.Ensure -eq 'Present') { 111 | if ($this.Replace) { 112 | if ($this.currentIdentities.Count -eq 0) { 113 | return $false 114 | } 115 | return $this.CompareAccountNames() 116 | } else { 117 | foreach ($identity in $this.AccountName) { 118 | if (-not $this.IsAssignedRight($identity)) { 119 | return $false 120 | } 121 | } 122 | } 123 | } elseif ($this.Ensure -eq 'Absent') { 124 | if ($this.AccountName.Count -eq 0 -and $userRight.AccountName) { 125 | return $false 126 | } elseif ($this.AccountName.Count -gt 0) { 127 | foreach ($identity in $this.AccountName) { 128 | if ($this.IsAssignedRight($identity)) { 129 | return $false 130 | } 131 | } 132 | } 133 | } 134 | 135 | return $true 136 | } catch { 137 | throw 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/public/Set-SecurityOption.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Management.Automation 2 | 3 | function Set-SecurityOption { 4 | <# 5 | .SYNOPSIS 6 | Set the value of a security option. 7 | .DESCRIPTION 8 | Set the value of a security option. 9 | .PARAMETER Value 10 | The value to set. 11 | .EXAMPLE 12 | Set-SecurityOption EnableLUA Enabled 13 | 14 | Enables the "User Account Control: Run all administrators in Admin Approval Mode" policy. 15 | .EXAMPLE 16 | Set-SecurityOption LegalNoticeText '' 17 | 18 | Sets the value of the LegalNoticeText policy to an empty string. 19 | .EXAMPLE 20 | 21 | #> 22 | 23 | [CmdletBinding(SupportsShouldProcess)] 24 | param ( 25 | # The name of the security option to set. 26 | [Parameter(Mandatory, Position = 1, ValueFromPipeline, ValueFromPipelineByPropertyName)] 27 | [String]$Name, 28 | 29 | # The value to set. 30 | [Parameter(Mandatory, Position = 2, ValueFromPipelineByPropertyName)] 31 | [AllowNull()] 32 | [AllowEmptyString()] 33 | [AllowEmptyCollection()] 34 | [Object]$Value 35 | ) 36 | 37 | process { 38 | if ($value -eq 'Not Defined') { 39 | $errorRecord = [ErrorRecord]::new( 40 | [ArgumentException]::new('Cannot set "Not Defined" for {0}. Please use Reset-SecurityOption' -f $Name), 41 | 'CannotUseSetToReset', 42 | 'InvalidArgument', 43 | $Value 44 | ) 45 | $pscmdlet.ThrowTerminatingError($errorRecord) 46 | } 47 | 48 | $securityOptionInfo = Resolve-SecurityOption $Name 49 | 50 | $valueType = $securityOptionInfo.ValueType -as [Type] 51 | if ($valueType.BaseType -eq [Enum]) { 52 | $parsedValue = 0 -as $valueType 53 | if ($valueType::TryParse([String]$Value, $true, [Ref]$parsedValue)) { 54 | $Value = $parsedValue 55 | } else { 56 | $errorRecord = [ErrorRecord]::new( 57 | [ArgumentException]::new(('{0} is not a valid value for {1}. Valid values are: {2}' -f 58 | $Value, 59 | $securityOptionInfo.ValueName, 60 | ([Enum]::GetNames($valueType) -join ', ') 61 | )), 62 | 'InvalidValueForSecurityOption', 63 | 'InvalidArgument', 64 | $Value 65 | ) 66 | $pscmdlet.ThrowTerminatingError($errorRecord) 67 | } 68 | } 69 | 70 | try { 71 | if ($securityOptionInfo.Key) { 72 | Write-Debug ('Registry value type: {0}' -f $securityOption.Name) 73 | 74 | if (Test-Path $securityOptionInfo.Key) { 75 | $key = Get-Item -Path $securityOptionInfo.Key 76 | } else { 77 | $key = New-Item -Path $securityOptionInfo.Key -ItemType Key -Force 78 | } 79 | 80 | if ($key.GetValueNames() -contains $securityOptionInfo.Name) { 81 | $currentValue = Get-ItemPropertyValue -Path $key.PSPath -Name $securityOptionInfo.Name 82 | 83 | $shouldSet = $false 84 | if ($value -is [Array] -or $currentValue -is [Array]) { 85 | $shouldSet = ($currentValue -join ' ') -ne ($value -join ' ') 86 | } elseif ($currentValue -ne $Value) { 87 | $shouldSet = $true 88 | } 89 | 90 | if ($shouldSet -and $pscmdlet.ShouldProcess(('Setting policy {0} to {1}' -f $securityOption.Name, $Value))) { 91 | Set-ItemProperty -Path $key.PSPath -Name $securityOptionInfo.Name -Value $Value 92 | } 93 | } else { 94 | $propertyType = switch ($securityOptionInfo.ValueType) { 95 | { $valueType.BaseType -eq [Enum] } { $_ = ($_ -as [Type]).GetEnumUnderlyingType().Name } 96 | 'Int32' { 'DWord'; break } 97 | 'Int64' { 'QWord'; break } 98 | 'String' { 'String'; break } 99 | 'String[]' { 'MultiString'; break } 100 | default { throw 'Invalid or unhandled registry property type' } 101 | } 102 | 103 | if ($pscmdlet.ShouldProcess(('Setting policy {0} to {1} with value type {2}' -f $securityOption.Name, $Value, $propertyType))) { 104 | New-ItemProperty -Path $key.PSPath -Name $securityOptionInfo.Name -Value $Value -PropertyType $propertyType 105 | } 106 | } 107 | } else { 108 | Write-Debug ('Class-handled value type: {0}' -f $securityOption.Name) 109 | 110 | $class = NewImplementingType $securityOptionInfo.Class 111 | $class.Value = $Value 112 | 113 | if (-not $class.Test()) { 114 | if ($pscmdlet.ShouldProcess(('Setting policy {0} to {1}' -f $securityOption.Name, $Value))) { 115 | $class.Set() 116 | } 117 | } 118 | } 119 | } catch { 120 | $pscmdlet.ThrowTerminatingError($_) 121 | } 122 | } 123 | } -------------------------------------------------------------------------------- /Indented.SecurityPolicy/test/dsc/SecurityOption.tests.ps1: -------------------------------------------------------------------------------- 1 | #region:TestFileHeader 2 | param ( 3 | [Boolean]$UseExisting 4 | ) 5 | 6 | if (-not $UseExisting) { 7 | $moduleBase = $psscriptroot.Substring(0, $psscriptroot.IndexOf('\test')) 8 | $stubBase = Resolve-Path (Join-Path $moduleBase 'test*\stub\*') 9 | if ($null -ne $stubBase) { 10 | $stubBase | Import-Module -Force 11 | } 12 | 13 | Import-Module $moduleBase -Force 14 | } 15 | #endregion 16 | 17 | InModuleScope Indented.SecurityPolicy { 18 | Describe SecurityOption { 19 | BeforeAll { 20 | Mock Get-SecurityOption { 21 | $securityOptionInfo = $Name | Resolve-SecurityOption 22 | [PSCustomObject]@{ 23 | Name = $securityOptionInfo.Name 24 | Description = $securityOptionInfo.Description 25 | Value = 'Enabled' 26 | PSTypeName = 'Indented.SecurityPolicy.SecurityOptionSetting' 27 | } 28 | } 29 | Mock Reset-SecurityOption 30 | Mock Set-SecurityOption 31 | } 32 | 33 | BeforeEach { 34 | $class = [SecurityOption]@{ 35 | Ensure = 'Present' 36 | Name = 'EnableLUA' 37 | Value = 'Enabled' 38 | } 39 | } 40 | 41 | Context 'Get' { 42 | It 'Gets the current state of the security option' { 43 | $class.Value = 'Disabled' 44 | $instance = $class.Get() 45 | 46 | $instance.Name | Should -Be 'EnableLUA' 47 | $instance.Description | Should -match 'Run all administrators in Admin Approval Mode' 48 | $instance.Value | Should -Be 'Enabled' 49 | } 50 | } 51 | 52 | Context 'Set' { 53 | It 'When ensure is present, calls Set-SecurityOption' { 54 | $class.Set() 55 | 56 | Assert-MockCalled Set-SecurityOption -Times 1 -Scope It 57 | } 58 | 59 | It 'When ensure is absent, calls Reset-SecurityOption' { 60 | $class.Ensure = 'Absent' 61 | $class.Set() 62 | 63 | Assert-MockCalled Reset-SecurityOption -Times 1 -Scope It 64 | } 65 | } 66 | 67 | Context 'Test, value matches' { 68 | It 'When ensure is present, and the value matches, returns true' { 69 | $class.Test() | Should -Be $true 70 | } 71 | 72 | It 'When ensure is absent, and the value matches the default, returns true' { 73 | $class.Ensure = 'Absent' 74 | 75 | $class.Test() | Should -Be $true 76 | } 77 | } 78 | 79 | Context 'Test, value does not match' { 80 | BeforeAll { 81 | Mock Get-SecurityOption { 82 | $securityOptionInfo = $Name | Resolve-SecurityOption 83 | [PSCustomObject]@{ 84 | Name = $securityOptionInfo.Name 85 | Description = $securityOptionInfo.Description 86 | Value = 'Disabled' 87 | PSTypeName = 'Indented.SecurityPolicy.SecurityOptionSetting' 88 | } 89 | } 90 | } 91 | 92 | It 'When ensure is present, and the value does not match, returns false' { 93 | $class.Test() | Should -Be $false 94 | } 95 | 96 | It ' When ensure is absent, and the value does not match the default, returns false' { 97 | $class.Ensure = 'Absent' 98 | 99 | $class.Test() | Should -Be $false 100 | } 101 | } 102 | 103 | Context 'Test, value is an array' { 104 | BeforeAll { 105 | Mock Get-SecurityOption { 106 | $securityOptionInfo = $Name | Resolve-SecurityOption 107 | [PSCustomObject]@{ 108 | Name = $securityOptionInfo.Name 109 | Description = $securityOptionInfo.Description 110 | Value = $Script:ReturnValue 111 | PSTypeName = 'Indented.SecurityPolicy.SecurityOptionSetting' 112 | } 113 | } 114 | } 115 | 116 | BeforeEach { 117 | $class.Name = 'NullSessionPipes' 118 | $class.Value = 'Value1', 'Value2' 119 | 120 | $Script:ReturnValue = [String[]]@() 121 | } 122 | 123 | It 'When ensure is present, and the value matches, returns true' { 124 | $Script:ReturnValue = [String[]]@('Value1', 'Value2') 125 | 126 | $class.Test() | Should -Be $true 127 | } 128 | 129 | It 'When ensure is present, and the value does not match, returns false' { 130 | $class.Test() | Should -Be $false 131 | } 132 | 133 | It 'When ensure is absent, and the value matches the default, returns true' { 134 | $class.Ensure = 'Absent' 135 | $class.Value = $null 136 | 137 | $class.Test() | Should -Be $true 138 | } 139 | 140 | It 'When ensure is absent, and the value does not match the default, returns false' { 141 | $Script:ReturnValue = [String[]]@('Value1', 'Value2') 142 | $class.Ensure = 'Absent' 143 | 144 | $class.Test() | SHould -Be $false 145 | } 146 | } 147 | } 148 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | 56 | # StyleCop 57 | StyleCopReport.xml 58 | 59 | # Files built by Visual Studio 60 | *_i.c 61 | *_p.c 62 | *_i.h 63 | *.ilk 64 | *.meta 65 | *.obj 66 | *.iobj 67 | *.pch 68 | *.pdb 69 | *.ipdb 70 | *.pgc 71 | *.pgd 72 | *.rsp 73 | *.sbr 74 | *.tlb 75 | *.tli 76 | *.tlh 77 | *.tmp 78 | *.tmp_proj 79 | *.log 80 | *.vspscc 81 | *.vssscc 82 | .builds 83 | *.pidb 84 | *.svclog 85 | *.scc 86 | 87 | # Chutzpah Test files 88 | _Chutzpah* 89 | 90 | # Visual C++ cache files 91 | ipch/ 92 | *.aps 93 | *.ncb 94 | *.opendb 95 | *.opensdf 96 | *.sdf 97 | *.cachefile 98 | *.VC.db 99 | *.VC.VC.opendb 100 | 101 | # Visual Studio profiler 102 | *.psess 103 | *.vsp 104 | *.vspx 105 | *.sap 106 | 107 | # Visual Studio Trace Files 108 | *.e2e 109 | 110 | # TFS 2012 Local Workspace 111 | $tf/ 112 | 113 | # Guidance Automation Toolkit 114 | *.gpState 115 | 116 | # ReSharper is a .NET coding add-in 117 | _ReSharper*/ 118 | *.[Rr]e[Ss]harper 119 | *.DotSettings.user 120 | 121 | # JustCode is a .NET coding add-in 122 | .JustCode 123 | 124 | # TeamCity is a build add-in 125 | _TeamCity* 126 | 127 | # DotCover is a Code Coverage Tool 128 | *.dotCover 129 | 130 | # AxoCover is a Code Coverage Tool 131 | .axoCover/* 132 | !.axoCover/settings.json 133 | 134 | # Visual Studio code coverage results 135 | *.coverage 136 | *.coveragexml 137 | 138 | # NCrunch 139 | _NCrunch_* 140 | .*crunch*.local.xml 141 | nCrunchTemp_* 142 | 143 | # MightyMoose 144 | *.mm.* 145 | AutoTest.Net/ 146 | 147 | # Web workbench (sass) 148 | .sass-cache/ 149 | 150 | # Installshield output folder 151 | [Ee]xpress/ 152 | 153 | # DocProject is a documentation generator add-in 154 | DocProject/buildhelp/ 155 | DocProject/Help/*.HxT 156 | DocProject/Help/*.HxC 157 | DocProject/Help/*.hhc 158 | DocProject/Help/*.hhk 159 | DocProject/Help/*.hhp 160 | DocProject/Help/Html2 161 | DocProject/Help/html 162 | 163 | # Click-Once directory 164 | publish/ 165 | 166 | # Publish Web Output 167 | *.[Pp]ublish.xml 168 | *.azurePubxml 169 | # Note: Comment the next line if you want to checkin your web deploy settings, 170 | # but database connection strings (with potential passwords) will be unencrypted 171 | *.pubxml 172 | *.publishproj 173 | 174 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 175 | # checkin your Azure Web App publish settings, but sensitive information contained 176 | # in these scripts will be unencrypted 177 | PublishScripts/ 178 | 179 | # NuGet Packages 180 | *.nupkg 181 | # The packages folder can be ignored because of Package Restore 182 | **/[Pp]ackages/* 183 | # except build/, which is used as an MSBuild target. 184 | !**/[Pp]ackages/build/ 185 | # Uncomment if necessary however generally it will be regenerated when needed 186 | #!**/[Pp]ackages/repositories.config 187 | # NuGet v3's project.json files produces more ignorable files 188 | *.nuget.props 189 | *.nuget.targets 190 | 191 | # Microsoft Azure Build Output 192 | csx/ 193 | *.build.csdef 194 | 195 | # Microsoft Azure Emulator 196 | ecf/ 197 | rcf/ 198 | 199 | # Windows Store app package directories and files 200 | AppPackages/ 201 | BundleArtifacts/ 202 | Package.StoreAssociation.xml 203 | _pkginfo.txt 204 | *.appx 205 | 206 | # Visual Studio cache files 207 | # files ending in .cache can be ignored 208 | *.[Cc]ache 209 | # but keep track of directories ending in .cache 210 | !*.[Cc]ache/ 211 | 212 | # Others 213 | ClientBin/ 214 | ~$* 215 | *~ 216 | *.dbmdl 217 | *.dbproj.schemaview 218 | *.jfm 219 | *.pfx 220 | *.publishsettings 221 | orleans.codegen.cs 222 | 223 | # Including strong name files can present a security risk 224 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 225 | #*.snk 226 | 227 | # Since there are multiple workflows, uncomment next line to ignore bower_components 228 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 229 | #bower_components/ 230 | 231 | # RIA/Silverlight projects 232 | Generated_Code/ 233 | 234 | # Backup & report files from converting an old project file 235 | # to a newer Visual Studio version. Backup files are not needed, 236 | # because we have git ;-) 237 | _UpgradeReport_Files/ 238 | Backup*/ 239 | UpgradeLog*.XML 240 | UpgradeLog*.htm 241 | ServiceFabricBackup/ 242 | *.rptproj.bak 243 | 244 | # SQL Server files 245 | *.mdf 246 | *.ldf 247 | *.ndf 248 | 249 | # Business Intelligence projects 250 | *.rdl.data 251 | *.bim.layout 252 | *.bim_*.settings 253 | *.rptproj.rsuser 254 | 255 | # Microsoft Fakes 256 | FakesAssemblies/ 257 | 258 | # GhostDoc plugin setting file 259 | *.GhostDoc.xml 260 | 261 | # Node.js Tools for Visual Studio 262 | .ntvs_analysis.dat 263 | node_modules/ 264 | 265 | # Visual Studio 6 build log 266 | *.plg 267 | 268 | # Visual Studio 6 workspace options file 269 | *.opt 270 | 271 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 272 | *.vbw 273 | 274 | # Visual Studio LightSwitch build output 275 | **/*.HTMLClient/GeneratedArtifacts 276 | **/*.DesktopClient/GeneratedArtifacts 277 | **/*.DesktopClient/ModelManifest.xml 278 | **/*.Server/GeneratedArtifacts 279 | **/*.Server/ModelManifest.xml 280 | _Pvt_Extensions 281 | 282 | # Paket dependency manager 283 | .paket/paket.exe 284 | paket-files/ 285 | 286 | # FAKE - F# Make 287 | .fake/ 288 | 289 | # JetBrains Rider 290 | .idea/ 291 | *.sln.iml 292 | 293 | # CodeRush 294 | .cr/ 295 | 296 | # Python Tools for Visual Studio (PTVS) 297 | __pycache__/ 298 | *.pyc 299 | 300 | # Cake - Uncomment if you are using it 301 | # tools/** 302 | # !tools/packages.config 303 | 304 | # Tabs Studio 305 | *.tss 306 | 307 | # Telerik's JustMock configuration file 308 | *.jmconfig 309 | 310 | # BizTalk build output 311 | *.btp.cs 312 | *.btm.cs 313 | *.odx.cs 314 | *.xsd.cs 315 | 316 | # OpenCover UI analysis results 317 | OpenCover/ 318 | 319 | # Azure Stream Analytics local run output 320 | ASALocalRun/ 321 | 322 | # MSBuild Binary and Structured Log 323 | *.binlog 324 | 325 | # NVidia Nsight GPU debugger configuration file 326 | *.nvuser 327 | 328 | # MFractors (Xamarin productivity tool) working folder 329 | .mfractor/ 330 | 331 | /build 332 | /.vscode 333 | /output 334 | /\d+ --------------------------------------------------------------------------------