Pester – Validate home directory share permissions

In our company a user’s home directory is created through a script. Unfortunately, the script had been somewhat broken for at least a few months. Home directories created during that time have wrong permissions and need to be fixed.

With hundreds of home directories this needs to be scripted – so I thought why not use Pester for that.

Describing Tests ACL of [\\homedrive\home$\jsp\]
   Context jsp
    [+] ACL should contain only 3 ACEs 66ms
    [+] ACL should contain [mydomain\fileradmins] 14ms
    [+] ACL should contain [mydomain\Domain-Admins] 13ms
    [+] ACL should contain [mydomain\jsp] 14ms
Describing Tests ACL of [\\homedrive\home$\jsp\MyScans]
   Context jsp
    [+] [\\homedrive\home$\jsp\MyScans] should exist 83ms
    [+] ACL should contain only 4 ACEs 9ms
    [+] ACL should contain [mydomain\printerserviceaccount] 15ms
Tests completed in 219ms
Passed: 7 Failed: 0 Skipped: 0 Pending: 0





Describing Tests ACL of [\\homedrive\home$\pfisterer\]
   Context pfisterer
    [-] ACL should contain only 3 ACEs 65ms
      Expected: {3}
      But was:  {4}
      at line: 10 in \\homedrive\home$\megamorf\gitlab\HomedrivePermissions\HomedrivePermissions.Tests.ps1
      10:             $Acl.Access.Count | Should Be 3
    [-] ACL should contain [mydomain\fileradmins] 14ms
      Expected: {FullControl}
      But was:  {}
      at line: 18 in \\homedrive\home$\megamorf\gitlab\HomedrivePermissions\HomedrivePermissions.Tests.ps1
      18:             $ACE.FileSystemRights  | Should Be 'FullControl'
    [-] ACL should contain [mydomain\Domain-Admins] 13ms
      Expected: {FullControl}
      But was:  {}
      at line: 30 in \\homedrive\home$\megamorf\gitlab\HomedrivePermissions\HomedrivePermissions.Tests.ps1
      30:             $ACE.FileSystemRights  | Should Be 'FullControl'
    [-] ACL should contain [mydomain\pfisterer] 14ms
      Expected: {Modify, Synchronize}
      But was:  {}
      at line: 42 in \\homedrive\home$\megamorf\gitlab\HomedrivePermissions\HomedrivePermissions.Tests.ps1
      42:             $ACE.FileSystemRights  | Should Be 'Modify, Synchronize'
Describing Tests ACL of [\\homedrive\home$\pfisterer\MyScans]
   Context pfisterer
    [+] [\\homedrive\home$\pfisterer\MyScans] should exist 110ms
    [-] ACL should contain only 4 ACEs 10ms
      Expected: {4}
      But was:  {5}
      at line: 66 in \\homedrive\home$\megamorf\gitlab\HomedrivePermissions\HomedrivePermissions.Tests.ps1
      66:             $Acl.Access.Count | Should Be 4
    [+] ACL should contain [mydomain\printerserviceaccount] 16ms
Tests completed in 244ms
Passed: 2 Failed: 5 Skipped: 0 Pending: 0

By default our home directories have Access Control Entries (ACEs) for the domain admins and storage admins groups with full access and the respective user account with modify permissions. For our printers’ “scan to home directory”-feature we have to ensure that a folder called MyScans exists in the user’s home directory. That folder needs an additional ACE of the printing service account with write permissions.


param($samaccountname)
Describe "Tests ACL of [\\homedrive\home$\$samaccountname\]" {
$ACL = Get-ACL "\\homedrive\home$\$samaccountname\"
Context "$samaccountname" {
It 'ACL should contain only 3 ACEs' {
$Acl.Access.Count | Should Be 3
}
It 'ACL should contain [mydomain\fileradmins]' {
$ACE = $ACL.Access | where {$_.IdentityReference -Like 'mydomain\fileradmins'}
$ACE | Should Not Be Null
$ACE.FileSystemRights | Should Be 'FullControl'
$ACE.AccessControlType | Should Be 'Allow'
$ACE.IsInherited | Should Be $False
$ACE.InheritanceFlags | Should Be 'ContainerInherit, ObjectInherit'
$ACE.PropagationFlags | Should Be 'None'
}
It 'ACL should contain [mydomain\Domain-Admins]' {
$ACE = $ACL.Access | where {$_.IdentityReference -Like 'mydomain\Domain-Admins'}
$ACE | Should Not Be Null
$ACE.FileSystemRights | Should Be 'FullControl'
$ACE.AccessControlType | Should Be 'Allow'
$ACE.IsInherited | Should Be $False
$ACE.InheritanceFlags | Should Be 'ContainerInherit, ObjectInherit'
$ACE.PropagationFlags | Should Be 'None'
}
It "ACL should contain [mydomain\$samaccountname]" {
$ACE = $ACL.Access | where {$_.IdentityReference -Like "mydomain\$samaccountname"}
$ACE | Should Not Be Null
$ACE.FileSystemRights | Should Be 'Modify, Synchronize'
$ACE.AccessControlType | Should Be 'Allow'
$ACE.IsInherited | Should Be $False
$ACE.InheritanceFlags | Should Be 'ContainerInherit, ObjectInherit'
$ACE.PropagationFlags | Should Be 'None'
}
}
}
Describe "Tests ACL of [\\homedrive\home$\$samaccountname\MyScans]" {
$ScanDir = "\\homedrive\home$\$samaccountname\MyScans"
$ACL = Get-ACL $ScanDir -ErrorAction SilentlyContinue
Context "$samaccountname" {
It "[\\homedrive\home$\$samaccountname\MyScans] should exist" {
Test-Path $ScanDir | Should Be $true
}
It 'ACL should contain only 4 ACEs' {
$Acl.Access.Count | Should Be 4
}
It 'ACL should contain [mydomain\printerserviceaccount]' {
$ACE = $ACL.Access | where {$_.IdentityReference -Like 'mydomain\printerserviceaccount'}
$ACE | Should Not Be Null
$ACE.FileSystemRights | Should Be 'Write, Synchronize'
$ACE.AccessControlType | Should Be 'Allow'
$ACE.IsInherited | Should Be $False
$ACE.InheritanceFlags | Should Be 'ContainerInherit, ObjectInherit'
$ACE.PropagationFlags | Should Be 'None'
}
}
}

Pester – Server deployment tests

Hey guys, here’s one of the basic tests I use to check if I’ve forgotten anything after deploying a server:

validate-postdeployment

As you can see the test accepts a Computername parameter. I’m using a wrapper function to run the test against a selection of computers like this:


$Computers = Get-ADComputer -Filter {Name -like "srvmgt*"}
$Test = "$PSScriptRoot\PostDeployment.Tests.ps1"
foreach( $Computer in ($Computers | Select -ExpandProperty DNSHostName | where{Test-Connection -Quiet -Count 1 -ComputerName $_} ) )
{
Invoke-Pester -Script @{ Path=$Test; Parameters= @{'ComputerName' = $Computer}} -Verbose #-PassThru
}


param($Computername)
Describe "Test-PostDeployment [$Computername]" {
It 'Tests if local wadmin account exists' {
<# #ADSI is too slow (~takes up to 30 seconds per Computer)
$wadmin = [ADSI] "WinNT://$Computername/wadmin,user"
$wadmin.Description | Should Be 'Local Administrator'
$wadmin.FullName | Should Be 'wadmin'
$wadmin.Name | Should Be 'wadmin'
#>
$wadmin = Get-WmiObject -Namespace "root\cimv2" -Query "SELECT * FROM Win32_UserAccount WHERE LocalAccount = True AND Name = 'wadmin'" -cn $Computername
$wadmin.Description | Should Be 'Local Administrator'
$wadmin.FullName | Should Be 'wadmin'
$wadmin.Name | Should Be 'wadmin'
$wadmin.Disabled | Should Be $false
$wadmin.Lockout | Should Be $false
$wadmin.PasswordRequired | Should Be $true
}
It 'Tests if wadmin is member of the local Administrators group' {
$group =[ADSI]"WinNT://$Computername/Administrators,group"
$members = foreach($entry in @($group.psbase.Invoke('Members')))
{
$entry.GetType().InvokeMember('Name', 'GetProperty', $null, $entry, $null)
}
$members -contains 'wadmin' | Should Be $true
}
It 'Should have Sophos AV installed' {
# too slow (takes up to 20 seconds per Computer)
# {Get-Service -Name savservice -ComputerName $Computername -ErrorAction Stop} | Should Not throw
$Params = @{
Namespace = "root\cimv2"
Query = "SELECT * FROM Win32_Service WHERE Name = 'savservice'"
}
$service = Get-WmiObject @Params -ComputerName $Computername
$service | Should Not BeNullOrEmpty
}
It 'Should have AD description' {
# computername = FQDN but we want the samaccountname to query AD
$samaccountname = $Computername.split('.')[0]
# no Active Directory module dependency
$Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$SearchRoot = $Domain.GetDirectoryEntry()
$objSearcher = [System.DirectoryServices.DirectorySearcher]$SearchRoot
$objSearcher.Filter = "(sAMAccountName=$samaccountname`$)"
$objSearcher.PropertiesToLoad.Add('distinguishedName') | Out-Null
$SearchResults = $objSearcher.FindAll()
if($SearchResults.Count -eq 1)
{
$DN = $SearchResults.properties.Item('distinguishedName')
$ADComputer = [ADSI]"LDAP://$DN"
[string]::IsNullOrEmpty($ADComputer.description) | Should Be $False
}
else
{
throw 'Computer not found in AD'
}
}
}

 

 

Pester – Operational validation

I’ve started using Pester for practical validation scenarios in our company. I recently discovered that someone from our SQL consulting company has disabled the firewall on all of our Windows Server 2012 R2 machines that run SQL Server 2014 – a thing that I’m not going to tolerate.

Here’s the firewall test code. You’ll see that I’m using a Pester feature called Testcases in order to minimize redundant code. Unfortunately, test cases are not described in the Pester wiki on github – I believe they’d be more widely used if that were the case.


param($ComputerName = $env:computername)
Describe 'Firewall is enabled' {
BeforeAll {
$session = New-PSSession -ComputerName $ComputerName
$Param = @{Session=$session}
}
$testCases = @(
@{
FWProfile = 'DomainProfile'
Description = 'Domain Profile'
}
@{
FWProfile = 'PublicProfile'
Description = 'Public Profile'
}
@{
FWProfile = 'StandardProfile'
Description = 'Private Profile'
}
)
It 'tests if the <Description> is enabled' -TestCases $testCases {
param ($FWProfile)
$Expected = 1
$Actual = Invoke-Command @Param -ScriptBlock { (Get-ItemProperty "HKLM:\System\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\${using:FWProfile}" -Name EnableFirewall).EnableFirewall }
$actual | Should Be $Expected
}
AfterAll {
Remove-PSSession -Session $session
}
}

PS ov:\> ls


    Directory: D:\Pester
          
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       14.02.2016     13:30           2000 FirewallStatus.Tests.ps1


PS ov:\> Invoke-Pester


Describing Firewall is enabled
        
 [+] tests if the Domain Profile is enabled 3.02s
 [+] tests if the Public Profile is enabled 199ms
 [+] tests if the Private Profile is enabled 211ms

Tests completed in 3.43s
Passed: 3 Failed: 0 Skipped: 0 Pending: 0 Inconclusive: 0
PS ov:\> Invoke-Pester -Script @{ Path = '.'; Parameters = @{ ComputerName = 'localhost' } }


Describing Firewall is enabled 

 [+] tests if the Domain Profile is enabled 2.95s
 [+] tests if the Public Profile is enabled 168ms
 [+] tests if the Private Profile is enabled 218ms

Tests completed in 3.34s
Passed: 3 Failed: 0 Skipped: 0 Pending: 0 Inconclusive: 0

Examples

Stay tuned for my post-deployment validation tests.