tests/Update-DbaSqlServiceAccount.Tests.ps1

$commandname = $MyInvocation.MyCommand.Name.Replace(".ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
. "$PSScriptRoot\..\internal\Connect-SqlInstance.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    
    $login = 'winLogin'
    $password = 'MyV3ry$ecur3P@ssw0rd'
    $securePassword = ConvertTo-SecureString $password -AsPlainText -Force
    $newPassword = 'Myxtr33mly$ecur3P@ssw0rd'
    $newSecurePassword = ConvertTo-SecureString $newPassword -AsPlainText -Force
    $server = Connect-SqlInstance -SqlInstance $script:instance2
    $computerName = $server.NetName
    $instanceName = $server.ServiceName
    $winLogin = "$computerName\$login"

    #cleanup
    $computer = [ADSI]"WinNT://$computerName"
    try {
        $user = [ADSI]"WinNT://$computerName/$login,user"
        if ($user.Name -eq $login) {
            $computer.Delete('User', $login)
        }
    }
    catch {<#User does not exist#>}
    
    if ($l = Get-DbaLogin -SqlInstance $script:instance2 -Login $winLogin) {
        $results = $server.Query("IF EXISTS (SELECT * FROM sys.server_principals WHERE name = '$winLogin') EXEC sp_who '$winLogin'")
        foreach ($spid in $results.spid) {
            $null = $server.Query("kill $spid")
        }
        if ($c = $l.EnumCredentials()) {
            $l.DropCredential($c)
        }
        $l.Drop()
    }

    #create Windows login
    $user = $computer.Create("user", $login)
    $user.SetPassword($password)
    $user.SetInfo()

    #Get current service users
    $services = Get-DbaSqlservice -ComputerName $script:instance2 -Type Engine, Agent -Instance $instanceName
    $currentAgentUser = ($services | Where-Object { $_.ServiceType -eq 'Agent' }).StartName
    $currentEngineUser = ($services | Where-Object { $_.ServiceType -eq 'Engine' }).StartName

    #Create a new sysadmin login on SQL Server
    $newLogin = New-Object Microsoft.SqlServer.Management.Smo.Login($server, $winLogin)
    $newLogin.LoginType = "WindowsUser"
    $newLogin.Create()
    $server.Roles['sysadmin'].AddMember($winLogin)

    $isRevertable = $true
    ForEach ($svcaccount in $currentAgentUser, $currentEngineUser) {
        if (! ($svcaccount.EndsWith('$') -or $svcaccount.StartsWith('NT AUTHORITY\') -or $svcaccount.StartsWith('NT Service\'))) {
            $isRevertable = $false
        }
    }
    
    Context "Current configuration to be able to roll back" {
        It "Both agent and engine services must exist" {
            ($services | Measure-Object).Count | Should Be 2
        }
        It "Current service accounts should be localsystem-like or MSA to allow for a rollback" {
            $isRevertable | Should be $true
        }
    }

    #Do not continue with the test if current configuration cannot be rolled back
    if (!$isRevertable) {
        Throw 'Current configuration cannot be rolled back - the test will not continue.'
    }

    Context "Set new service account for SQL Services" {


        $errVar = $warnVar = $null
        $cred = New-Object System.Management.Automation.PSCredential($login, $securePassword)
        $results = Update-DbaSqlServiceAccount -ComputerName $computerName -ServiceName $services.ServiceName -ServiceCredential $cred -ErrorVariable $errVar -WarningVariable $warnVar
        
        It "Should return something" {    
            $results | Should Not Be $null
        }
        It "Should have no errors or warnings" {
            $errVar | Should Be $null
            $warnVar | Should Be $null
        }
        It "Should be successful" {
            foreach ($result in $results) {
                $result.Status | Should Be 'Successful'
                $result.State | Should Be 'Running'
                $result.StartName | Should Be ".\$login"
            }
        }
    }

    Context "Change password of the service account" {
        #Change the password
        ([adsi]"WinNT://$computerName/$login,user").SetPassword($newPassword)

        $errVar = $warnVar = $null
        $results = $services | Sort-Object ServicePriority | Update-DbaSqlServiceAccount -Password $newSecurePassword -ErrorVariable $errVar -WarningVariable $warnVar

        It "Password change should return something" {    
            $results | Should Not Be $null
        }
        It "Should have no errors or warnings" {
            $errVar | Should Be $null
            $warnVar | Should Be $null
        }
        It "Should be successful" {
            foreach ($result in $results) {
                $result.Status | Should Be 'Successful'
                $result.State | Should Be 'Running'
            }
        }

        $results = Get-DbaSqlService -ComputerName $computerName -ServiceName $services.ServiceName | Restart-DbaSqlService
        It "Service restart should return something" {    
            $results | Should Not Be $null
        }
        It "Service restart should be successful" {
            foreach ($result in $results) {
                $result.Status | Should Be 'Successful'
                $result.State | Should Be 'Running'
            }
        }
    }

    Context "Change agent service account to local system" {
        $errVar = $warnVar = $null
        $results = $services | Where-Object { $_.ServiceType -eq 'Agent' } | Update-DbaSqlServiceAccount -Username 'NT AUTHORITY\LOCAL SYSTEM' -ErrorVariable $errVar -WarningVariable $warnVar
        
        It "Should return something" {    
            $results | Should Not Be $null
        }
        It "Should have no errors or warnings" {
            $errVar | Should Be $null
            $warnVar | Should Be $null
        }
        It "Should be successful" {
            foreach ($result in $results) {
                $result.Status | Should Be 'Successful'
                $result.State | Should Be 'Running'
                $result.StartName | Should Be 'LocalSystem'
            }
        }
    }
    Context "Revert SQL Agent service account changes ($currentAgentUser)" {
        $errVar = $warnVar = $null
        $results = $services | Where-Object { $_.ServiceType -eq 'Agent' } | Update-DbaSqlServiceAccount -Username $currentAgentUser -ErrorVariable $errVar -WarningVariable $warnVar
        
        It "Should return something" {    
            $results | Should Not Be $null
        }
        It "Should have no errors or warnings" {
            $errVar | Should Be $null
            $warnVar | Should Be $null
        }
        It "Should be successful" {
            foreach ($result in $results) {
                $result.Status | Should Be 'Successful'
                $result.State | Should Be 'Running'
                $result.StartName | Should Be $currentAgentUser
            }
        }
    }
    Context "Revert SQL Engine service account changes ($currentEngineUser)" {
        $errVar = $warnVar = $null
        $results = $services | Where-Object { $_.ServiceType -eq 'Engine' } | Update-DbaSqlServiceAccount -Username $currentEngineUser -ErrorVariable $errVar -WarningVariable $warnVar
        
        It "Should return something" {    
            $results | Should Not Be $null
        }
        It "Should have no errors or warnings" {
            $errVar | Should Be $null
            $warnVar | Should Be $null
        }
        It "Should be successful" {
            foreach ($result in $results) {
                $result.Status | Should Be 'Successful'
                $result.State | Should Be 'Running'
                $result.StartName | Should Be $currentEngineUser
            }
        }

    }

    #Cleanup
    $server.Logins[$winLogin].Drop()
    $computer.Delete('User', $login) 
}