cEPRSDBFeatures.psm1

enum Ensure
{
    Present
    Absent
}

[DscResource()]
class cEPRSDBSnapShot
{
    [DscProperty(Key)]
    [String]$ServerName

    [DscProperty(Mandatory=$true)]
    [String]$DatabaseName

    [DscProperty(Mandatory=$true)]
    [String]$SnapshotName

    [DscProperty(Mandatory=$true)]
    [String]$Logfile

    [cEPRSDBSnapShot]Get()
    {
        Write-Verbose "Getting database connection..."
        $qdbname = SQLCMD -S $this.ServerName -d $this.DatabaseName -Q "set nocount on; select db_name();"
        $dbname = $this.DatabaseName
        $snapshot = $this.SnapshotName
        $logfilepath = $this.Logfile
    
        if($qdbname -Contains $this.DatabaseName)
        {
            $dbname = ""
        }
    
        Write-Verbose "Getting detected values..."
        $returnValue = @{
            ServerName = $this.ServerName
            DatabaseName = $dbname
            SnapshotName = $this.SnapshotName
            Logfile = $logfilepath
        }
        return $this
    }
    [bool]Test()
    {
        #Write-Verbose "Use this cmdlet to deliver information about command processing."

        #Write-Debug "Use this cmdlet to write debug information while troubleshooting."

        [Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMo") | Out-Null
        [Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMoExtended") | Out-Null
        [Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMoEnum") | Out-Null
        [Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.ConnectionInfo") | Out-Null

        Write-Verbose "Connecting to $($this.ServerName) server- START" | Out-File $this.LogFile -Append
        $srvr = new-object ("Microsoft.SqlServer.Management.Smo.Server") "$($this.ServerName)"
        Write-Verbose "Connecting to $($this.ServerName) server- END" | Out-File $this.LogFile -Append

        Write-Verbose "Checking for existence of DB $($this.DatabaseName) in $($this.ServerName) server- START" | Out-File $($this.Logfile) -Append
        #$CheckDB=$srvr.Databases | where {$_.name -eq "$DatabaseName"}
        $database = $srvr.Databases[$this.DatabaseName]  
        Write-Verbose "Checking for existence of DB $($this.DatabaseName) in $($this.ServerName) server- END" | Out-File $($this.Logfile) -Append
        $DBName=$database.Name
        Write-Verbose "DB name is $DBName" | Out-File $this.Logfile -Append

        if($database.Name)
        {
            $result = $false
        }   
        else
        {
            Write-Verbose "Database $($this.DatabaseName) doesnot exist" 
            $result = $true
        }
        Write-Verbose "$result"
        return $result
    }
    Set()
    {
        Write-Verbose "Executing the SQL file on Server: $($this.ServerName) in Database: $($this.DatabaseName)."        
        New-Item -Path $this.Logfile -ItemType File -Force -Verbose
        Write-Verbose "After File create"
        CreateDB_Snapshot -ServerName $this.ServerName -DatabaseName $this.DatabaseName -DBSnapshotName $this.SnapshotName -LogFile $this.Logfile
    }
    
}

Function CreateDB_Snapshot
{
param($ServerName, $DatabaseName, $DBSnapshotName, $LogFile)
     Try
     {
          [Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMo") | Out-Null
          [Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMoExtended") | Out-Null
          [Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMoEnum") | Out-Null
          [Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.ConnectionInfo") | Out-Null 

          Write-Verbose "Connecting to $ServerName server- START" | Out-File $LogFile -Append
          $srvr = new-object ("Microsoft.SqlServer.Management.Smo.Server") "$ServerName"
          Write-Verbose "Connecting to $ServerName server- END" | Out-File $LogFile -Append

          Write-Verbose "Checking for existence of DB $DatabaseName in $ServerName server- START" | Out-File $LogFile -Append
          $database=$srvr.Databases | where {$_.name -eq "$DatabaseName"}
          #$database = $srvr.Databases[$DatabaseName]
          Write-Verbose "Checking for existence of DB $DatabaseName in $ServerName server- END" | Out-File $LogFile -Append
          $DBName=$database.Name
          Write-Verbose "DB name is $DBName" | Out-File $LogFile -Append

          
             Write-Verbose "------------------------------------------------------------------" | Out-File $LogFile -Append
             Write-Verbose  "Creating '$DBSnapshotName' snapshot on '$DatabaseName' database ." | Out-File $LogFile -Append
             
                $snapshot = New-Object ("Microsoft.SqlServer.Management.Smo.Database") ($ServerName,$DBSnapshotName)
                $snapshot.DatabaseSnapshotBaseName=$database.Name            

                #Add all filegroups from the base database to snapshot
                foreach ($filegroup in $database.FileGroups) {
                 $newfg = New-Object ("Microsoft.SqlServer.Management.Smo.FileGroup") ($snapshot,$filegroup.Name)
                 Write-Verbose  "newfg = $newfg"
                 $snapshot.FileGroups.Add($newfg)
                }            

                #Add all datafiles from each filegroup of the base database to snapshot with a new datafile name
                #.ss as the datafile extension for the snapshot file
                #By default, the snapshot files will reside in the same folder as base DB data files
                foreach ($filegroup in $database.FileGroups) {
                 foreach ($datafile in $filegroup.Files) {
                  $newDataFile = New-Object ("Microsoft.SqlServer.Management.Smo.DataFile") ($snapshot.FileGroups[$filegroup.Name],$datafile.Name,"$($database.PrimaryFilePath)\$($datafile.Name).ss")
                  $newDataFile.FileName
                  
                  $newDataFilename=$newDataFile.FileName
                   Write-Verbose  "newDataFile = $newDataFilename"
                  $snapshot.FileGroups[$filegroup.Name].Files.Add($newDataFile)
                 }
                }            

                #Create the snapshot
                Write-Verbose  "Creating '$DBSnapshotName' snapshot-START." | Out-File $LogFile -Append
                $snapshot.Create("$DBSnapshotName") | Out-File $LogFile -Append
                Write-Verbose  "Creating '$DBSnapshotName' snapshot-END." | Out-File $LogFile -Append

             Write-Verbose "------------------------------------------------------------------" | Out-File $LogFile -Append
             Write-Verbose "Creating DB Snapshot finished." | Out-File $LogFile -Append
             
          
     }
     Catch
     {
         [System.Exception]
         Write-Error $_.Exception.Message | Out-File $LogFile -Append
     }
     Finally
     {
         Write-Verbose "Execution completed!" | Out-File $LogFile -Append
     }
}

[DscResource()]
class cEPRSRestoreSnapshot
{
    [DscProperty(Key)]
    [String]$ServerName

    [DscProperty(Mandatory=$true)]
    [String]$DatabaseName

    [DscProperty(Mandatory=$true)]
    [String]$SnapShotName

    [DscProperty(Mandatory=$true)]
    [String]$LogFile

    [cEPRSRestoreSnapshot]Get()
    {
        Write-Verbose "Getting database connection..."
        $qdbname = SQLCMD -S $this.ServerName -d $this.DatabaseName -Q "set nocount on; select db_name();"
        $dbname = $this.DatabaseName
        $snapshot = $this.SnapShotName
        $logfilepath = $this.LogFile
    
        if($qdbname -Contains $this.DatabaseName)
        {
            $dbname = ""
        }
        $this.DatabaseName = $dbname
        return $this
    }
    [bool]Test()
    {
        $result=$false
        [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null
        $srvr = new-object ("Microsoft.SqlServer.Management.Smo.Server") "$($this.ServerName)"
        $CheckDB=$srvr.Databases | where {$_.name -eq "$($this.DatabaseName)"}

        if($CheckDB.Name -ne "$($this.DatabaseName)")
        {
        Write-Error "$($this.DatabaseName) Database not found in server $($this.ServerName)!"
        $result = $true
        }
    
        return $result

    }
    Set()
    {
        Write-Verbose "Executing the SQL file on Server: $($this.ServerName) in Database: $($this.DatabaseName)."        
        New-Item -Path $this.LogFile -itemtype "file" -Force
    
        RestoreDB_Snapshot -ServerName $this.ServerName -DatabaseName $this.DatabaseName -DBSnapshotName $this.SnapShotName -LogFile $this.SnapShotName
    }
}

Function RestoreDB_Snapshot
{
    param($ServerName, $DatabaseName, $DBSnapshotName, $LogFile)
     Try
     {
          [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null
          $srvr = new-object ("Microsoft.SqlServer.Management.Smo.Server") "$ServerName"
          $CheckDB=$srvr.Databases | where {$_.name -eq "$DatabaseName"}
          if($CheckDB.Name)
          {
             Write-Verbose "Restoring '$DatabaseName' database by '$DBSnapshotName' snapshot." 
             
             $db = $srvr.Databases["$DatabaseName"]
             $SQLScript= "RESTORE DATABASE $DatabaseName FROM DATABASE_SNAPSHOT = '$DBSnapshotName'"
             $db.ExecuteNonQuery($SQLScript) | Out-File $LogFile -Append

             Write-Verbose "Restoration finished." 
             
          }
          else
          {
               Write-Verbose "Database '$DBSnapshotName' does not exists!" | Out-File $LogFile -Append
          }
     }
     Catch
     {
         [System.Exception]
         Write-Error  $_.Exception.Message 
     }
     Finally
     {
         Write-Verbose "Execution completed!" 
     }
}

[DSCResource()]

class cEPRSKillDBSessions
{

    [DscProperty(Key)] [String] $ServerName
    [DscProperty(Key)] [String] $DatabaseName
        

    [cEPRSKillDBSessions] Get()
    {
       try
       {
        [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
        $SQlSvr1 = New-Object "Microsoft.SqlServer.Management.Smo.Server" "$($this.ServerName)"
        $DesiredRule=$SQlSvr1.Databases | where {$_.name -eq "$($this.DatabaseName)"}
        $Presence = if($DesiredRule){"Present"}else{"Absent"}
          

        $output = @{
                Ensure = $Presence;
                ServerName = $this.ServerName;
                DatabaseName = $this.DatabaseName;
                }

        return $output
        }
        Finally
        {
           $this.DesiredRule.Dispose() 
        }
    }
    
    [bool] Test()
    {
       $Result = $false
        [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
        $SQlSvr1 = New-Object "Microsoft.SqlServer.Management.Smo.Server" "$($this.ServerName)"
        Write-Verbose "$($this.ServerName); $($this.DatabaseName)" -Verbose
        $DesiredRule=$SQlSvr1.Databases | where {$_.name -eq "$($this.DatabaseName)"}
        $Presence = if($DesiredRule){"Present"}else{"Absent"}
        
        if($Presence -eq $this.Ensure)
        {
            $Result = $false
        }
        else
        {
            $Result = $true
        }
        return $Result
    }
    
    
    [void] Set()
    {
        try
        {
            # Killing the existing DB sessions
            [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
            $SQlSvr1 = New-Object "Microsoft.SqlServer.Management.Smo.Server" "$($this.ServerName)"
            $SQlSvr1.KillAllprocesses("$($this.DatabaseName)")
        }
        Catch [system.exception]
        {        
            Write-Verbose $_.exception.message    -Verbose
        }
        
        }
                                                                    
     
}

[DSCResource()]
class cEPRSRestoreDBFromBackup
{
    [DscProperty(Key)]
    [String]
    $SQLServer

    [DscProperty(Mandatory = $true)]
    [String]
    $SQLDatabase

    [DscProperty(Mandatory = $true)]
    [String]
    $BakfilePath
        
    [DscProperty(Mandatory = $true)]
    [bool]
    $TrustedConnection

    [DscProperty(Mandatory = $false)]
    [String]
    $SQLusername
        
    [DscProperty(Mandatory = $false)]
    [String]
    $SQLpassword


    

    [cEPRSRestoreDBFromBackup] Get()
    {
        Write-Verbose "Getting database connection..."
        $qdbname = SQLCMD -S $this.SQLServer -d $this.SQLDatabase -Q "set nocount on; select db_name();"
        $dbname = $this.SQLDatabase

        $backfile = $this.BakfilePath
    
        if($qdbname -Contains $this.SQLDatabase)
        {
            $dbname = ""
        }
    

        $returnValue = @{
            ServerName = $this.SQLServer
            DatabaseName = $dbname
            BackupFile = $backfile

        }
        return $this
    }

    [bool] Test()
    {
        $result=$false
        [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null
        $srvr = new-object ("Microsoft.SqlServer.Management.Smo.Server") "$($this.SQLServer)"
        $CheckDB=$srvr.Databases | where {$_.name -eq "$($this.SQLDatabase)"}
        
        $BakExists = Test-Path "$($this.BakfilePath)" -ErrorAction SilentlyContinue

        if($BakExists -eq $true)
        {
            $result = $false
        }
        else
        {
            $result = $true
        }
        return $result
    }

    Set()
    {
        Write-Verbose "Restoring Database $($this.SQLDatabase) on $($this.SQLServer) from $($this.BakfilePath)"
        Restore-SQLdatabase -SQLServer $this.SQLServer -SQLDatabase $this.SQLDatabase -BakfilePath $this.BakfilePath -TrustedConnection $this.TrustedConnection -SQLusername "$($this.SQLusername)" -SQLpassword $this.SQLpassword
    }
}
Function Restore-SQLdatabase
{
param(
    $SQLServer,
    $SQLDatabase,
    $BakfilePath,
    $TrustedConnection,
    $SQLusername,
    $SQLPassword
)
    Try
    {
        if ($TrustedConnection -eq $false)
        {
            if($SQLUsername -eq "")
            {
                write-warning "The SQL Username variable must be defined, if 'TrustedConnection' is false. Use '-SQLusername' to define username."
                break
            }
            if($SQLPassword -eq "")
            {
                write-warning "The SQL Password variable must be defined, if 'TrustedConnection' is false. Use '-SQLPassword' to define username."
                break
            }
        }

     

        $SQLConn = New-Object System.Data.SQLClient.SQLConnection

        #checks for a trusted SQL connection
        if($TrustedConnection -eq $false)
        {
            $UserNameSplit = $SQLusername.split("\")
            $UserName = $UserNameSplit[1]
            $Domain = $UserNameSplit[0]
            $Testcredentials=ValidateUserCredentials -Domain $Domain -Username $UserName -Password $SQLPassword
            if($Testcredentials.IsValid -eq "False")
            {   
                Write-Error "User credentials are not correct. Please check the values provided."
                exit -1
            }
            #Using an SQL account for login
            $SQLConn.ConnectionString = "Data Source=$SQLServer;User ID=$SQLusername;password=$SQLpassword;Initial Catalog=master;Integrated Security=True;Trusted_Connection=True;"
        }
        Else
        {
            #Using a trusted connection
            $SQLConn.ConnectionString = "Server=$SQLServer; Trusted_Connection=True"
        }
        Write-verbose "Attempting to connect to the Specified SQL server:" 
        try{
            $SQLConn.Open()
            Write-verbose "Success" 
            }
        catch{
                Write-warning "An exception was caught while attempting to open the SQL connection, please confirm the login details are correct and try again."
                Break
            }
      
        $SQLCmd = New-Object System.Data.SQLClient.SQLCommand
        $SQLcmd = $SQLconn.CreateCommand()
        $sqlcmd.commandtimeout=0
        $SQLcmd.CommandText="ALTER DATABASE $SQLDatabase SET SINGLE_USER WITH ROLLBACK IMMEDIATE RESTORE DATABASE $SQLDatabase FROM DISK = '$BakfilePath' WITH REPLACE"
        $starttime = Get-date
        try{
                $SQLcmd.Executenonquery() | out-null
                $result="Success"
                Write-Verbose "Database restoration finished!" 
            }
        catch{
                write-warning "An Exception was caught while restoring the database!"
                write-warning "$_"
                write-warning "attempting to recover the database"
                $SQLcmd.CommandText="ALTER DATABASE $SQLDatabase SET MULTI_USER"
                $SQLcmd.Executenonquery() | out-null
                $result="Failed"
            }
        finally{
                $SQLconn.close()
                $timetaken=[math]::round(((get-date) - $starttime).totalseconds,0)
                $report=new-object PSObject -Property @{
                SQLServer=$SQLserver;
                Database=$sqlDatabase;
                Result=$result;
                Timetaken="$timetaken Seconds";}
                }
        Return $Report
    }
    Catch
    {
        [System.Exception]
        Write-Verbose $_.Exception.Message 
    }
    Finally
    {
        Write-Verbose "Execution completed!" 
       
    }
}

Function ValidateUserCredentials
{
param($Domain, $Username, $Password)
    Try
    {
         Add-Type -AssemblyName System.DirectoryServices.AccountManagement
         $ct = [System.DirectoryServices.AccountManagement.ContextType]::Domain
         $pc = New-Object System.DirectoryServices.AccountManagement.PrincipalContext($ct, $Domain)
         New-Object PSObject -Property @{
         UserName = $Username;
         IsValid = $pc.ValidateCredentials($Username, $Password,"Negotiate").ToString()
         }
        
    }
    Catch
    {
         [System.Exception]
         Write-Host $_.Exception.Message
    }
    Finally
    {
         "User credentials validation finished."
         [System.Console]::WriteLine("User credentials validation finished.")
    }
    
}

[DSCResource()]
class cEPRSExecuteSql
{

[DscProperty(Key)] [String] $ServerName
[DscProperty(Key)] [String] $DatabaseName
[DscProperty(Key)] [String] $SQLFile
[DscProperty(Key)] [String] $Logfile
[DscProperty()] [String] $Variables

[cEPRSExecuteSql] Get()
{
    Write-Verbose "Getting database connection..." -Verbose
    $qdbname= SQLCMD -S $this.ServerName -d $this.DatabaseName -Q "set nocount on; select db_name();"
    $dbname=$this.DatabaseName 
    $sqlfilepath=$this.SQLFile
    $logfilepath=$this.Logfile
            
     if($qdbname -Contains $this.DatabaseName)
        {
            $dbname = ""
        }
    Write-Verbose "Getting SQL file path..." -Verbose

        if(-not(Test-Path -Path "$($this.SQLFile)" -Include *.sql))
        {
            $sqlfilepath = ""
        }
    Write-Verbose "Getting detected values..." -Verbose

        $returnValue = @{
            ServerName = $this.ServerName
            DatabaseName = $dbname
            SQLFile = $sqlfilepath
            Logfile = $logfilepath
        }

        return $returnValue
    }

    set()
    {
 
        Write-Verbose "Executing the SQL file on Server: $($this.ServerName) in Database: $($this.DatabaseName)."
        New-Item -Path $this.Logfile -itemtype "file" -Force
        $executable = 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe'

        if($this.Variables -eq '' -or $this.Variables -eq $null)
        {
            Write-Verbose "if part"

            $arglist = "SQLCMD.EXE -S $($this.ServerName) -d $($this.DatabaseName) -E -b -i `"$($this.SQLFile)`" | Out-File -FilePath `"$($this.Logfile)`""
            Write-Verbose "arglist: $arglist"
            $proc = Start-Process $executable -ArgumentList "$arglist" -Wait -NoNewWindow -PassThru
                if($proc.HasExited)
                {
                    Write-Verbose "SQLCMD.EXE execution process exited."
                } 
        }
        else
        {
            Write-Verbose "else part"
            Write-Verbose "Variables: this.Variables."
            $arglistVar = "SQLCMD.EXE -S $($this.ServerName) -d $($this.DatabaseName) -E -b -i `"$($this.SQLFile)`" -v $($this.Variables) | Out-File -FilePath `"$($this.Logfile)`""
            Write-Verbose "arglistVar: $arglistVar"
            $proc = Start-Process $executable -ArgumentList "$arglistVar" -Wait -NoNewWindow -PassThru
            if($proc.HasExited)
            {
                Write-Verbose "SQLCMD.EXE execution process exited."
            }
        }

            Write-Verbose "Execution completed. Examine the log file, $($this.Logfile)"
      
    }


    [bool] Test()
    {
        $result = $false
        Write-Verbose "Verifying execution..." -Verbose
        if(Test-Path -Path "$($this.Logfile)")
        {
            $wordsToFind  = {"error","fail","aborted","rolled back","stopped"}
            $log = Get-Content $this.Logfile
            foreach($word in $wordsToFind)
            {
                $containsWord = $log | %{$_ -like $word}
            }

            If($this.containsWord -contains $true)
            {
                Write-Verbose "SQL file failed to execute successfully. Examine the log file, $($this.Logfile)"
                return $false
            }        
        }
    
        Write-Verbose "Verification completed. ...." -Verbose
        Write-Verbose "$result" -Verbose
        return $result
    }
}