functions/SharedFunctions.ps1

# These are shared, mostly internal functions.

Function Update-dbareports
{
<#
.SYNOPSIS
Exported function. Updates dbareports. Deletes current copy and replaces it with freshest copy.

.EXAMPLE
Update-dbareports
#>
    
    
    Invoke-Expression (Invoke-WebRequest -UseBasicParsing http://git.io/vn1hQ).Content
}

<#
                
        All functions below are internal to the module and cannot be executed via command line.
                
#>


Function Connect-SqlServer
{
<#
.SYNOPSIS
Internal function that creates SMO server object. Input can be text or SMO.Server.
#>
    
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object]$SqlServer,
        [System.Management.Automation.PSCredential]$SqlCredential,
        [switch]$ParameterConnection,
        [switch]$RegularUser
    )
    
    
    if ($SqlServer.GetType() -eq [Microsoft.SqlServer.Management.Smo.Server])
    {
        
        if ($ParameterConnection)
        {
            $paramserver = New-Object Microsoft.SqlServer.Management.Smo.Server
            $paramserver.ConnectionContext.ConnectTimeout = 2
            $paramserver.ConnectionContext.ApplicationName = "dbareports PowerShell module - dbareports.io"
            $paramserver.ConnectionContext.ConnectionString = $SqlServer.ConnectionContext.ConnectionString
            
            if ($SqlCredential.username -ne $null)
            {
                $username = ($SqlCredential.username).TrimStart("\")
                
                if ($username -like "*\*")
                {
                    $username = $username.Split("\")[1]
                    $authtype = "Windows Authentication with Credential"
                    $server.ConnectionContext.LoginSecure = $true
                    $server.ConnectionContext.ConnectAsUser = $true
                    $server.ConnectionContext.ConnectAsUserName = $username
                    $server.ConnectionContext.ConnectAsUserPassword = ($SqlCredential).GetNetworkCredential().Password
                }
                else
                {
                    $authtype = "SQL Authentication"
                    $server.ConnectionContext.LoginSecure = $false
                    $server.ConnectionContext.set_Login($username)
                    $server.ConnectionContext.set_SecurePassword($SqlCredential.Password)
                }
            }
            
            $paramserver.ConnectionContext.Connect()
            return $paramserver
        }
        
        if ($SqlServer.ConnectionContext.IsOpen -eq $false)
        {
            $SqlServer.ConnectionContext.Connect()
        }
        return $SqlServer
    }
    
    $server = New-Object Microsoft.SqlServer.Management.Smo.Server $SqlServer
    $server.ConnectionContext.ApplicationName = "dbareports PowerShell module - dbareports.io"
    
    try
    {
        if ($SqlCredential.username -ne $null)
        {
            $username = ($SqlCredential.username).TrimStart("\")
            
            if ($username -like "*\*")
            {
                $username = $username.Split("\")[1]
                $authtype = "Windows Authentication with Credential"
                $server.ConnectionContext.LoginSecure = $true
                $server.ConnectionContext.ConnectAsUser = $true
                $server.ConnectionContext.ConnectAsUserName = $username
                $server.ConnectionContext.ConnectAsUserPassword = ($SqlCredential).GetNetworkCredential().Password
            }
            else
            {
                $authtype = "SQL Authentication"
                $server.ConnectionContext.LoginSecure = $false
                $server.ConnectionContext.set_Login($username)
                $server.ConnectionContext.set_SecurePassword($SqlCredential.Password)
            }
        }
    }
    catch { }
    
    try
    {
        if ($ParameterConnection)
        {
            $server.ConnectionContext.ConnectTimeout = 10
        }
        else
        {
            $server.ConnectionContext.ConnectTimeout = 11
        }
        
        $server.ConnectionContext.Connect()
    }
    catch
    {
        $message = $_.Exception.InnerException.InnerException
        $message = $message.ToString()
        $message = ($message -Split '-->')[0]
        $message = ($message -Split 'at System.Data.SqlClient')[0]
        $message = ($message -Split 'at System.Data.ProviderBase')[0]
        throw "Can't connect to $sqlserver`: $message "
    }
    
    if ($RegularUser -eq $false)
    {
        if ($server.ConnectionContext.FixedServerRoles -notmatch "SysAdmin")
        {
            throw "Not a sysadmin on $SqlServer. Quitting."
        }
    }
    
    if ($ParameterConnection -eq $false)
    {
        if ($server.VersionMajor -eq 8)
        {
            # 2000
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Database], 'ReplicationOptions', 'Collation', 'CompatibilityLevel', 'CreateDate', 'ID', 'IsAccessible', 'IsFullTextEnabled', 'IsUpdateable', 'LastBackupDate', 'LastDifferentialBackupDate', 'LastLogBackupDate', 'Name', 'Owner', 'PrimaryFilePath', 'ReadOnly', 'RecoveryModel', 'Status', 'Version')
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Login], 'CreateDate', 'DateLastModified', 'DefaultDatabase', 'DenyWindowsLogin', 'IsSystemObject', 'Language', 'LanguageAlias', 'LoginType', 'Name', 'Sid', 'WindowsLoginAccessType')
        }
        
        
        elseif ($server.VersionMajor -eq 9 -or $server.VersionMajor -eq 10)
        {
            # 2005 and 2008
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Database], 'ReplicationOptions', 'BrokerEnabled', 'Collation', 'CompatibilityLevel', 'CreateDate', 'ID', 'IsAccessible', 'IsFullTextEnabled', 'IsMirroringEnabled', 'IsUpdateable', 'LastBackupDate', 'LastDifferentialBackupDate', 'LastLogBackupDate', 'Name', 'Owner', 'PrimaryFilePath', 'ReadOnly', 'RecoveryModel', 'Status', 'Trustworthy', 'Version')
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Login], 'AsymmetricKey', 'Certificate', 'CreateDate', 'Credential', 'DateLastModified', 'DefaultDatabase', 'DenyWindowsLogin', 'ID', 'IsDisabled', 'IsLocked', 'IsPasswordExpired', 'IsSystemObject', 'Language', 'LanguageAlias', 'LoginType', 'MustChangePassword', 'Name', 'PasswordExpirationEnabled', 'PasswordPolicyEnforced', 'Sid', 'WindowsLoginAccessType')
        }
        
        else
        {
            # 2012 and above
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Database], 'ReplicationOptions', 'ActiveConnections', 'AvailabilityDatabaseSynchronizationState', 'AvailabilityGroupName', 'BrokerEnabled', 'Collation', 'CompatibilityLevel', 'ContainmentType', 'CreateDate', 'ID', 'IsAccessible', 'IsFullTextEnabled', 'IsMirroringEnabled', 'IsUpdateable', 'LastBackupDate', 'LastDifferentialBackupDate', 'LastLogBackupDate', 'Name', 'Owner', 'PrimaryFilePath', 'ReadOnly', 'RecoveryModel', 'Status', 'Trustworthy', 'Version')
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Login], 'AsymmetricKey', 'Certificate', 'CreateDate', 'Credential', 'DateLastModified', 'DefaultDatabase', 'DenyWindowsLogin', 'ID', 'IsDisabled', 'IsLocked', 'IsPasswordExpired', 'IsSystemObject', 'Language', 'LanguageAlias', 'LoginType', 'MustChangePassword', 'Name', 'PasswordExpirationEnabled', 'PasswordHashAlgorithm', 'PasswordPolicyEnforced', 'Sid', 'WindowsLoginAccessType')
        }
    }
    
    return $server
}

Function Test-dbrSqlPath
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlInstance")]
        [object]$SqlServer,
        [Parameter(Mandatory = $true)]
        [string]$Path,
        [System.Management.Automation.PSCredential]$SqlCredential
    )
    
    $server = Connect-SqlServer -SqlServer $SqlServer -SqlCredential $SqlCredential
    $sql = "EXEC master.dbo.xp_fileexist '$path'"
    $fileexist = $server.ConnectionContext.ExecuteWithResults($sql)
    
    if ($fileexist.tables.rows['File Exists'] -eq $true -or $fileexist.tables.rows['File is a Directory'] -eq $true)
    {
        return $true
    }
    else
    {
        return $false
    }
}

Function Test-SqlConnection
{
<#
.SYNOPSIS
Exported function. Tests a the connection to a single instance and shows the output.

.EXAMPLE
Test-SqlConnection sql01

Sample output:

Local PowerShell Enviornment

Windows : 10.0.10240.0
PowerShell : 5.0.10240.16384
CLR : 4.0.30319.42000
SMO : 13.0.0.0
DomainUser : True
RunAsAdmin : False

SQL Server Connection Information

ServerName : sql01
BaseName : sql01
InstanceName : (Default)
AuthType : Windows Authentication (Trusted)
ConnectingAsUser : ad\dba
ConnectSuccess : True
SqlServerVersion : 12.0.2370
AddlConnectInfo : N/A
RemoteServer : True
IPAddress : 10.0.1.4
NetBIOSname : SQLSERVER2014A
RemotingAccessible : True
Pingable : True
DefaultSQLPortOpen : True
RemotingPortOpen : True
#>
    
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlInstance")]
        [object]$SqlServer,
        [object]$SqlCredential
    )
    
    $username = $SqlCredential.username
    if ($username -ne $null)
    {
        $username = $username.TrimStart("\")
        if ($username -like "*\*") { throw "Only SQL Logins can be specified when using the Credential parameter. To connect as to SQL Server a different Windows user, you must start PowerShell as that user." }
    }
    
    # Get local enviornment
    Write-Output "Getting local enivornment information"
    $localinfo = @{ } | Select-Object Windows, PowerShell, CLR, SMO, DomainUser, RunAsAdmin
    $localinfo.Windows = [environment]::OSVersion.Version.ToString()
    $localinfo.PowerShell = $PSVersionTable.PSversion.ToString()
    $localinfo.CLR = $PSVersionTable.CLRVersion.ToString()
    $smo = (([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.Fullname -like "Microsoft.SqlServer.SMO,*" }).FullName -Split ", ")[1]
    $localinfo.SMO = $smo.TrimStart("Version=")
    $localinfo.DomainUser = $env:computername -ne $env:USERDOMAIN
    $localinfo.RunAsAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
    
    # SQL Server
    if ($SqlServer.GetType() -eq [Microsoft.SqlServer.Management.Smo.Server]) { $SqlServer = $SqlServer.Name.ToString() }
    
    $serverinfo = @{ } | Select-Object ServerName, BaseName, InstanceName, AuthType, ConnectingAsUser, ConnectSuccess, SqlServerVersion, AddlConnectInfo, RemoteServer, IPAddress, NetBIOSname, RemotingAccessible, Pingable, DefaultSQLPortOpen, RemotingPortOpen
    
    $serverinfo.ServerName = $sqlserver
    
    Write-Output "Determining SQL Server base address"
    $baseaddress = $sqlserver.Split("\")[0]
    try { $instance = $sqlserver.Split("\")[1] }
    catch { $instance = "(Default)" }
    if ($instance -eq $null) { $instance = "(Default)" }
    
    if ($baseaddress -eq "." -or $baseaddress -eq $env:COMPUTERNAME)
    {
        $ipaddr = "."
        $hostname = $env:COMPUTERNAME
        $baseaddress = $env:COMPUTERNAME
    }
    
    $serverinfo.BaseName = $baseaddress
    $remote = $baseaddress -ne $env:COMPUTERNAME
    $serverinfo.InstanceName = $instance
    $serverinfo.RemoteServer = $remote
    
    Write-Output "Resolving IP address"
    try
    {
        $hostentry = [System.Net.Dns]::GetHostEntry($baseaddress)
        $ipaddr = ($hostentry.AddressList | Where-Object { $_ -notlike '169.*' } | Select-Object -First 1).IPAddressToString
    }
    catch { $ipaddr = "Unable to resolve" }
    
    $serverinfo.IPAddress = $ipaddr
    
    Write-Output "Resolving NetBIOS name"
    try
    {
        $hostname = (Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE -ComputerName $ipaddr -ErrorAction SilentlyContinue).PSComputerName
        if ($hostname -eq $null) { $hostname = (nbtstat -A $ipaddr | Where-Object { $_ -match '\<00\> UNIQUE' } | ForEach-Object { $_.SubString(4, 14) }).Trim() }
    }
    catch { $hostname = "Unknown" }
    
    $serverinfo.NetBIOSname = $hostname
    
    
    if ($remote -eq $true)
    {
        # Test for WinRM #Test-WinRM neh
        Write-Output "Checking remote acccess"
        winrm id -r:$hostname 2>$null | Out-Null
        if ($LastExitCode -eq 0) { $remoting = $true }
        else { $remoting = $false }
        
        $serverinfo.RemotingAccessible = $remoting
        
        Write-Output "Testing raw socket connection to PowerShell remoting port"
        $tcp = New-Object System.Net.Sockets.TcpClient
        try
        {
            $tcp.Connect($baseaddress, 135)
            $tcp.Close()
            $tcp.Dispose()
            $remotingport = $true
        }
        catch { $remotingport = $false }
        
        $serverinfo.RemotingPortOpen = $remotingport
    }
    
    # Test Connection first using Test-Connection which requires ICMP access then failback to tcp if pings are blocked
    Write-Output "Testing ping to $baseaddress"
    $testconnect = Test-Connection -ComputerName $baseaddress -Count 1 -Quiet
    
    $serverinfo.Pingable = $testconnect
    
    # SQL Server connection
    
    if ($instance -eq "(Default)")
    {
        Write-Output "Testing raw socket connection to default SQL port"
        $tcp = New-Object System.Net.Sockets.TcpClient
        try
        {
            $tcp.Connect($baseaddress, 1433)
            $tcp.Close()
            $tcp.Dispose()
            $sqlport = $true
        }
        catch { $sqlport = $false }
        $serverinfo.DefaultSQLPortOpen = $sqlport
    }
    else { $serverinfo.DefaultSQLPortOpen = "N/A" }
    
    $server = New-Object Microsoft.SqlServer.Management.Smo.Server $SqlServer
    
    try
    {
        if ($SqlCredential -ne $null)
        {
            $authtype = "SQL Authentication"
            $username = ($SqlCredential.username).TrimStart("\")
            $server.ConnectionContext.LoginSecure = $false
            $server.ConnectionContext.set_Login($username)
            $server.ConnectionContext.set_SecurePassword($SqlCredential.Password)
        }
        else
        {
            $authtype = "Windows Authentication (Trusted)"
            $username = "$env:USERDOMAIN\$env:username"
        }
    }
    catch
    {
        $authtype = "Windows Authentication (Trusted)"
        $username = "$env:USERDOMAIN\$env:username"
    }
    
    $serverinfo.ConnectingAsUser = $username
    $serverinfo.AuthType = $authtype
    
    
    Write-Output "Attempting to connect to $SqlServer as $username "
    try
    {
        $server.ConnectionContext.ConnectTimeout = 10
        $server.ConnectionContext.Connect()
        $connectSuccess = $true
        $version = $server.Version.ToString()
        $addlinfo = "N/A"
        $server.ConnectionContext.Disconnect()
    }
    catch
    {
        $connectSuccess = $false
        $version = "N/A"
        $addlinfo = $_.Exception
    }
    
    $serverinfo.ConnectSuccess = $connectSuccess
    $serverinfo.SqlServerVersion = $version
    $serverinfo.AddlConnectInfo = $addlinfo
    
    Write-Output "`nLocal PowerShell Enviornment"
    $localinfo | Select-Object Windows, PowerShell, CLR, SMO, DomainUser, RunAsAdmin
    
    Write-Output "SQL Server Connection Information`n"
    $serverinfo | Select-Object ServerName, BaseName, InstanceName, AuthType, ConnectingAsUser, ConnectSuccess, SqlServerVersion, AddlConnectInfo, RemoteServer, IPAddress, NetBIOSname, RemotingAccessible, Pingable, DefaultSQLPortOpen, RemotingPortOpen
    
}

Function Connect-AsServer
{
<#
.SYNOPSIS
Internal function that creates SMO server object. Input can be text or SMO.Server.
#>
    
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object]$AsServer,
        [switch]$ParameterConnection
    )
    
    if ($AsServer.GetType() -eq [Microsoft.AnalysisServices.Server])
    {
        
        if ($ParameterConnection)
        {
            $paramserver = New-Object Microsoft.AnalysisServices.Server
            $paramserver.Connect("Data Source=$($AsServer.Name);Connect Timeout=2")
            return $paramserver
        }
        
        if ($AsServer.Connected -eq $false) { $AsServer.Connect("Data Source=$($AsServer.Name);Connect Timeout=3") }
        return $AsServer
    }
    
    $server = New-Object Microsoft.AnalysisServices.Server
    
    try
    {
        if ($ParameterConnection)
        {
            $server.Connect("Data Source=$AsServer;Connect Timeout=2")
        }
        else { $server.Connect("Data Source=$AsServer;Connect Timeout=3") }
    }
    catch
    {
        $message = $_.Exception.InnerException
        $message = $message.ToString()
        $message = ($message -Split '-->')[0]
        $message = ($message -Split 'at System.Data.SqlClient')[0]
        $message = ($message -Split 'at System.Data.ProviderBase')[0]
        throw "Can't connect to $asserver`: $message "
    }
    
    return $server
}

Function Invoke-SmoCheck
{
<#
.SYNOPSIS
Checks for PowerShell SMO version vs SQL Server's SMO version.

#>
    
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object]$SqlServer
    )
    
    if ($script:smocheck -ne $true)
    {
        $script:smocheck = $true
        $smo = (([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.Fullname -like "Microsoft.SqlServer.SMO,*" }).FullName -Split ", ")[1]
        $smo = ([version]$smo.TrimStart("Version=")).Major
        $serverversion = $SqlServer.version.major
        
        if ($serverversion - $smo -gt 1)
        {
            Write-Warning "Your version of SMO is $smo, which is significantly older than $($sqlserver.name)'s version $($SqlServer.version.major)."
            Write-Warning "This may present an issue when migrating certain portions of SQL Server."
            Write-Warning "If you encounter issues, consider upgrading SMO."
        }
    }
}

Function Get-SqlDefaultPaths
{
<#
.SYNOPSIS
Internal function. Returns the default data and log paths for SQL Server. Needed because SMO's server.defaultpath is sometimes null.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlInstance")]
        [object]$SqlServer,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$filetype,
        [object]$SqlCredential
    )
    
    $server = Connect-SqlServer -SqlServer $SqlServer -SqlCredential $SqlCredential
    
    switch ($filetype) { "mdf" { $filetype = "data" } "ldf" { $filetype = "log" } }
    
    if ($filetype -eq "log")
    {
        # First attempt
        $filepath = $server.DefaultLog
        # Second attempt
        if ($filepath.Length -eq 0) { $filepath = $server.Information.MasterDbLogPath }
        # Third attempt
        if ($filepath.Length -eq 0)
        {
            $sql = "select SERVERPROPERTY('InstanceDefaultLogPath') as physical_name"
            $filepath = $server.ConnectionContext.ExecuteScalar($sql)
        }
    }
    else
    {
        # First attempt
        $filepath = $server.DefaultFile
        # Second attempt
        if ($filepath.Length -eq 0) { $filepath = $server.Information.MasterDbPath }
        # Third attempt
        if ($filepath.Length -eq 0)
        {
            $sql = "select SERVERPROPERTY('InstanceDefaultDataPath') as physical_name"
            $filepath = $server.ConnectionContext.ExecuteScalar($sql)
        }
    }
    
    if ($filepath.Length -eq 0) { throw "Cannot determine the required directory path" }
    $filepath = $filepath.TrimEnd("\")
    return $filepath
}

Function Get-SqlSaLogin
{
<#
.SYNOPSIS
Internal function. Gets the name of the sa login in case someone changed it.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlInstance")]
        [object]$SqlServer,
        [object]$SqlCredential
    )
    $server = Connect-SqlServer -SqlServer $SqlServer -SqlCredential $SqlCredential
    $sa = $server.Logins | Where-Object { $_.id -eq 1 }
    
    return $sa.name
    
}

Function Join-AdminUnc
{
<#
.SYNOPSIS
Internal function. Parses a path to make it an admin UNC.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$servername,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$filepath
        
    )
    
    if (!$filepath) { return }
    if ($filepath.StartsWith("\\")) { return $filepath }
    
    $servername = $servername.Split("\")[0]
    
    if ($filepath.length -gt 0 -and $filepath -ne [System.DbNull]::Value)
    {
        $newpath = Join-Path "\\$servername\" $filepath.replace(':', '$')
        return $newpath
    }
    else { return }
}

Function Test-SqlSa
{
<#
.SYNOPSIS
Internal function. Ensures sysadmin account access on SQL Server.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlInstance")]
        [object]$SqlServer,
        [object]$SqlCredential
    )
    
    try
    {
        
        if ($SqlServer.GetType() -eq [Microsoft.SqlServer.Management.Smo.Server])
        {
            return ($SqlServer.ConnectionContext.FixedServerRoles -match "SysAdmin")
        }
        
        $server = Connect-SqlServer -SqlServer $SqlServer -SqlCredential $SqlCredential
        return ($server.ConnectionContext.FixedServerRoles -match "SysAdmin")
    }
    catch { return $false }
}

Function Resolve-NetBiosName
{
 <#
.SYNOPSIS
Internal function. Takes a best guess at the NetBIOS name of a server.
 #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlInstance")]
        [object]$SqlServer,
        [object]$SqlCredential
    )
    
    $server = Connect-SqlServer -SqlServer $SqlServer -SqlCredential $SqlCredential
    $servernetbios = $server.ComputerNamePhysicalNetBIOS
    
    if ($servernetbios -eq $null)
    {
        $servernetbios = ($server.name).Split("\")[0]
        $servernetbios = $servernetbios.Split(",")[0]
    }
    
    return $($servernetbios.ToLower())
}

Function Resolve-SqlIpAddress
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlInstance")]
        [object]$SqlServer,
        [object]$SqlCredential
    )
    
    $server = Connect-SqlServer -SqlServer $SqlServer -SqlCredential $SqlCredential
    $servernetbios = $server.ComputerNamePhysicalNetBIOS
    $ipaddr = (Test-Connection $servernetbios -count 1).Ipv4Address
    return $ipaddr
}

Function Resolve-IpAddress
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlInstance")]
        [object]$ComputerName
    )
    
    $ipaddr = (Test-Connection $ComputerName -count 1).Ipv4Address
    return $ipaddr
}


Function Test-SqlAgent
{
<#
.SYNOPSIS
Internal function. Checks to see if SQL Server Agent is running on a server.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlInstance")]
        [object]$SqlServer,
        [object]$SqlCredential
    )
    
    if ($SqlServer.GetType() -ne [Microsoft.SqlServer.Management.Smo.Server])
    {
        $SqlServer = Connect-SqlServer -SqlServer $SqlServer -SqlCredential $SqlCredential
    }
    
    if ($SqlServer.JobServer -eq $null) { return $false }
    try { $null = $SqlServer.JobServer.script(); return $true }
    catch { return $false }
}

Function Get-SaLoginName
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlInstance")]
        [object]$SqlServer,
        [object]$SqlCredential
    )
    
    
    $server = Connect-SqlServer -SqlServer $SqlServer -SqlCredential $SqlCredential
    $saname = ($server.logins | Where-Object { $_.id -eq 1 }).Name
    
    return $saname
}

Function Write-Exception
{
<#
.SYNOPSIS
Internal function. Writes exception to disk (my docs\dbareports-exceptions.txt) for later analysis.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object]$e
    )
    
    $docs = [Environment]::GetFolderPath("mydocuments")
    $errorlog = "$docs\dbareports-exceptions.txt"
    $message = $e.Exception
    $infocation = $e.InvocationInfo
    
    $position = $infocation.PositionMessage
    $scriptname = $infocation.ScriptName
    if ($e.Exception.InnerException -ne $null) { $messsage = $e.Exception.InnerException }
    
    $message = $message.ToString()
    
    Add-Content $errorlog $(Get-Date)
    Add-Content $errorlog $scriptname
    Add-Content $errorlog $position
    Add-Content $errorlog $message
    Write-Warning "See error log $(Resolve-Path $errorlog) for more details."
}

Function New-DbrAgentJobCategory
{
    param ([string]$CategoryName,
        $JobServer)
    if (!$JobServer.JobCategories[$CategoryName])
    {
        try
        {
            Write-Log -path $LogFilePath  -message "Creating Agent Job Category $CategoryName" -Level Info
            $Category = New-Object Microsoft.SqlServer.Management.Smo.Agent.JobCategory
            $Category.Parent = $JobServer
            $Category.Name = $CategoryName
            $Category.Create()
            Write-Log -path $LogFilePath  -message "Created Agent Job Category $CategoryName" -Level Info
        }
        catch
        {

            Write-Log -path $LogFilePath  -message "FAILED : To Create Agent Job Category $CategoryName - Aborting - $_" -Level Warn
            continue
        }
    }
}


function Get-Instances
{
    $sql = "SELECT DISTINCT ServerName, InstanceName, InstanceId,Serverid FROM [dbo].[InstanceList] Where Inactive = 0 AND NotContactable = 0"
    try
    {
        $server = $sourceserver.Databases[$InstallDatabase].ExecuteWithResults($sql).Tables
    }
    catch
    {
        Write-Exception $_
        throw "Can't get InstanceList in the $InstallDatabase database on $($sourceserver.name)."
    }
    
    return $server
}

function Get-ExtendedProperties
{
    $sql = "SELECT name, value FROM fn_listextendedproperty(default, default, default, default, default, default, default);"
    try
    {
        $property = $sourceserver.Databases[$InstallDatabase].ExecuteWithResults($sql).Tables
    }
    catch
    {
        Write-Exception $_
        throw "Can't get extended properties from $InstallDatabase on $($sourceserver.name)."
    }
    
    return $property
}
function Initialize-DataTable
{
    # Create datatable for inserts, based off of schema information from existing table
    
    $schema = $table.Split(".")[0]
    $tablename = $table.Split(".")[1]
    
    $sql = "SELECT COLUMN_NAME,DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS where TABLE_SCHEMA = '$schema' AND TABLE_NAME = '$tablename'"
    
    try
    {
        $results = $sourceserver.Databases[$InstallDatabase].ExecuteWithResults($sql)
    }
    catch
    {
        Write-Exception $_
        throw "Can't get column list from $table in the $InstallDatabase database on $($sourceserver.name)."
    }
    
    $script:datatable = New-Object System.Data.DataTable $table
foreach ($result in $results.Tables.rows) 
    { 
       $ColumnName = $result.column_name 
       if ($result.data_type -eq 'datetime') 
       { 
              $Column = New-Object system.Data.DataColumn $ColumnName, ([datetime]) 
            Write-Output "Added $ColumnName of datetime"
       }
        elseif($result.data_type -eq 'int')
        {
            $Column = New-Object system.Data.DataColumn $ColumnName, ([int])
            Write-Output "Added $ColumnName of int" 
        } 
        elseif($result.data_type -eq 'datetime2')
        {
            $Column = New-Object system.Data.DataColumn $ColumnName, ([datetime]) 
            Write-Output "Added $ColumnName of datetime"
        } 
        elseif($result.data_type -eq 'bit')
        {
            $Column = New-Object system.Data.DataColumn $ColumnName, ([boolean]) 
            Write-Output "Added $ColumnName of bit"
        } 
        elseif($result.data_type -eq 'float')
        {
            $Column = New-Object system.Data.DataColumn $ColumnName, ([float]) 
            Write-Output "Added $ColumnName of float"
        } 
       else 
       { 
              $Column = New-Object system.Data.DataColumn $ColumnName, ([string]) 
            Write-Output "Added $ColumnName of string"
       } 
       $null = $datatable.Columns.Add($column) 
    }  #end foreach
}# end Initialize-DataTable

Function Write-Tvp
{
    $cmd = $sourceserver.ConnectionContext.SqlConnectionObject.CreateCommand()
    $cmd.CommandType = "StoredProcedure"
    $cmd.CommandText = "$schema.usp_$tablename"
    $null = $cmd.Parameters.Add("@TVP", [System.Data.SqlDbType]::Structured)
    $cmd.Parameters["@TVP"].Value = $datatable
    $null = $cmd.ExecuteNonQuery()
}

Function Get-ConfigFileName
{
    $docs = [Environment]::GetFolderPath("MyDocuments")
    $folder = "$docs\WindowsPowerShell\Modules\dbareports"
    $configfile = "$folder\dbareports-config.json"
    $exists = Test-Path $configfile
    
    if ($exists -eq $true)
    {
        return $configfile
    }
    else
    {
        $folderexists = Test-Path $folder
        
        if ($folderexists -eq $false)
        {
            $null = New-Item -ItemType Directory $folder -Force -ErrorAction Ignore
        }
        return $configfile
    }
}

Function Get-Config
{
    $config = Get-Content -Raw -Path (Get-ConfigFileName) -ErrorAction SilentlyContinue | ConvertFrom-Json
    
    if ($config.SqlServer.length -eq 0)
    {
        throw "No config file found. Have you installed dbareports? Please run Install-DbaReports or Install-DbaReportsClient"
    }
    
    if ($config.username.length -gt 0)
    {
        $username = $config.Username
        $password = $config.SecurePassword | ConvertTo-SecureString
        $tempcred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $password
        Set-Variable -Name SqlCredential -Value $tempcred -Scope Script
    }
    
    Set-Variable -Name SqlServer -Value $config.sqlserver -Scope Script
    Set-Variable -Name InstallDatabase -Value $config.InstallDatabase -Scope Script
}