functions/Add-DbrServerToInventory.ps1
Function Add-DbrServerToInventory { <# .SYNOPSIS Adds an instance or an array of instances to the dbareports database using the config file .DESCRIPTION This command will add a SQL Instance or an array of instances to the dbareports database using the config file generated at install or via the dbrclient command .PARAMETER SqlInstance The instance or array of instances to add .PARAMETER SqlInstanceCredential The credential to connect to the dbareports database .PARAMETER Port The Port of the Instance to be added (if not specified then this is gathered) .PARAMETER Environment The terminology that you and your users use to define the environment the instance is in. Suggested examples are Prod or Production, Test, UAT, QA, PreProd, ProductionSupport,Development, etc .PARAMETER Location The terminology that you and your users use to define the location of the instance. It could be the town or city that the data centre is in or the name of the office etc .PARAMETER Confirm Prompts you for confirmation before executing the command. .PARAMETER WhatIf This doesnt work as install is too dynamic. Show what would happen if the cmdlet was run. .NOTES dbareports PowerShell module (https://dbareports.io, SQLDBAWithABeard.com) Copyright (C) 2016 Rob Sewell This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. .LINK https://dbareports.io/functions/Add-DbrServerToInventory .EXAMPLE Add-DbrServerToInventory sql2016 Adds the SQL Server instance "sql2016" to the inventory then takes additional steps determined by the content of the config file. .EXAMPLE Add-DbrServerToInventory sql2016, sql2014 Adds the SQL Server instances sql2016 and sql2014 to the inventory then takes additional steps determined by the content of the config file. #> [CmdletBinding(SupportsShouldProcess = $true)] [OutputType([string])] Param ( [parameter(ValueFromPipeline = $true, Mandatory = $true)] [Alias("SqlServer", "ServerInstance")] [string[]]$SqlInstance, [object]$SqlInstanceCredential, [int]$Port, [string]$Environment, [string]$Location ) BEGIN { try { $docs = [Environment]::GetFolderPath("MyDocuments") $Date = Get-Date -format yyyyMMddhhmmss If ($PSCmdlet.ShouldProcess("Creating LogFile")) { $LogFile = New-Item "$docs\dbareports_ADD-DBRServerToInventory_$Date.txt" -ItemType File -ErrorAction Stop } } catch { Write-Warning "Failed to create log file please see error below" Write-Error $_ Write-Output "You can find the install log here $($Logfile.FullName)- IF it managed to create it!" break } $LogFilePath = $LogFile.FullName Write-Output "Log filepath for install is $LogFilePath" try { Get-Config } catch { Write-Log -path $LogFilePath -message "Get-Config Failed to run - $_" -Level Error Write-Output "Something went wrong - The Beard is sad :-( . You can find the install log here $($Logfile.FullName)" } $SqlServer = $script:SqlServer $InstallDatabase = $script:InstallDatabase $SqlCredential = $script:SqlCredential if ($SqlServer.length -eq 0) { Write-Log -path $LogFilePath -message "No config file found. Have you installed dbareports? Please run Install-DbaReports or Install-DbaReportsClient" -Level Warn Write-Output "Something went wrong - The Beard is sad :-( . You can find the install log here $($Logfile.FullName)" } try { If ($PSCmdlet.ShouldProcess("Connecting to $sqlserver")) { $sourceserver = Connect-SqlServer -SqlServer $sqlserver -SqlCredential $SqlCredential } } catch { Write-Log -path $LogFilePath -message "Failed to connect to $sqlserver - $_ " -Level Error Write-Output "Something went wrong - The Beard is sad :-( . You can find the install log here $($Logfile.FullName)" break } # Get columns automatically from the table on the SQL Server # and creates the necessary $script:datatable with it $table = "dbo.InstanceList" $schema = $table.Split(".")[0] $tablename = $table.Split(".")[1] try { If ($PSCmdlet.ShouldProcess("Initialise the Datatable")) { Initialize-DataTable Write-Log -path $LogFilePath -message "Intialised the datatable for $table" -Level Info $Column = New-Object system.Data.DataColumn update, ([boolean]) $null = $datatable.Columns.Add($column) Write-Log -path $LogFilePath -message "Added update of bit" -Level Info } } catch { Write-Log -path $LogFilePath -message "Failed to Intialise the datatable for $table - $_ " -Level Error Write-Output "Something went wrong - The Beard is sad :-( . You can find the install log here $($Logfile.FullName)" break } # Go get list of instances try { $sql = "SELECT * FROM [dbo].[InstanceList]" $allservers = $sourceserver.Databases[$InstallDatabase].ExecuteWithResults($sql).Tables } catch { Write-Log -path $LogFilePath -message "Can't get InstanceList in the $InstallDatabase database on $($sourceserver.name). - $_" -Level Error Write-Output "Something went wrong - The Beard is sad :-( . You can find the install log here $($Logfile.FullName)" break } # Go get a list of SERVERS :-) try { $sql = "SELECT ServerID,ServerName FROM [info].[ServerInfo]" $TheServers = $sourceserver.Databases[$InstallDatabase].ExecuteWithResults($sql).Tables } catch { Write-Log -path $LogFilePath -message "Can't get ServerName in the $InstallDatabase database on $($sourceserver.name). - $_" -Level Error Write-Output "Something went wrong - The Beard is sad :-( . You can find the install log here $($Logfile.FullName)" break } } PROCESS { foreach ($server in $SqlInstance) { try { try { $smoserver = Connect-SqlServer -SqlServer "TCP:$Server" -SqlCredential $SqlInstanceCredential } catch { Write-Log -path $LogFilePath -message "TCP connection failed for $Server trying again without" -Level Warn $smoserver = Connect-SqlServer -SqlServer "$Server" -SqlCredential $SqlInstanceCredential } # If localhost or . entered and server being added then $port is not retrieved unless connection made with server name if ($smoserver.ConnectionContext.ServerInstance -eq 'localhost' -or $smoserver.ConnectionContext.ServerInstance -eq '.' ) { try { $smoserver.ConnectionContext.Disconnect() $Connection = "TCP:$($smoserver.ComputerNamePhysicalNetBIOS)" $smoserver = New-Object Microsoft.SqlServer.Management.Smo.Server $Connection } catch { Write-Log -path $LogFilePath -message "TCP connection failed for TCP:$($smoserver.ComputerNamePhysicalNetBIOS)" -Level Error Write-Output "Something went wrong - The Beard is sad :-( . You can find the install log here $($Logfile.FullName)" break } } $ComputerName = $smoserver.ComputerNamePhysicalNetBIOS if ($null -eq $ComputerName) { $ComputerName = $smoserver.DomainInstanceName } $ServerName = $smoserver.NetName if ($null -eq $ServerName) { $ServerName = $smoserver.DomainInstanceName } $isclustered = $smoserver.IsClustered $InstanceName = $smoserver.InstanceName $name = $smoserver.Name.Replace("TCP:","") $NotContactable = $False # Write-Output "ComputerName = $ComputerName" ## For troubleshooting if ($InstanceName.length -eq 0) { $InstanceName = "MSSQLSERVER" } } catch { Write-Log -path $LogFilePath -message "Couldn't contact $Server. Marked as NotContactable." -Level Warn $NotContactable = $true $Name = $server } $row = $allservers.Rows | Where-Object { $_.InstanceName -eq $InstanceName -and $_.ComputerName -eq $ComputerName } $key = $row.InstanceId if ($null -eq $key) { $update = $false } else { Write-Log -path $LogFilePath -message "$Server already exists in database. Updating information." -Level Warn $update = $true } $row = $TheServers.Rows | Where-Object {$_.ServerName -eq $ComputerName } if($row.Count -gt 1) { Write-Log -Path $LogFilePath -Message "You appear to have duplicate entries for this ComputerName. Please Check Instance List table and resolve" -Level Error Write-Output "Something went wrong - The Beard is sad :-( . You can find the install log here $($Logfile.FullName)" break } $Serverkey = $row.ServerId $Exists = $false if($datatable.rows.count -gt 0) { $exists = $datatable.Rows.ComputerName.Contains($ComputerName) } else { $exists = $false } # Write-output "Exists = $Exists for $Server" ## trouble shooting if ($null -eq $Serverkey) { if($exists -eq $false) { $update = $false try { $sql = "INSERT INTO [info].[ServerInfo] ([ServerName]) VALUES ('$ComputerName')" If ($PSCmdlet.ShouldProcess("Adding $ComputerName to $InstallDatabase")) { $sourceserver.Databases[$InstallDatabase].ExecuteNonQuery($sql) } Write-Output "Added $ComputerName to ServerInfo Table" } catch { Write-Log -path $LogFilePath -message "Server insert FOR $Servername failed - $_" -Level Error Write-Output "Something went wrong - The Beard is sad :-( . You can find the install log here $($Logfile.FullName)" break } } else { Write-Log -path $LogFilePath -message "$ComputerName already exists in datatable. Moving On." -Level Info break } } else { Write-Log -path $LogFilePath -message "$ComputerName already exists in database. Updating information." -Level info } try { $sql = "SELECT [ServerId] FROM [info].[ServerInfo] WHERE Servername = '$ComputerName'" # Write-Output $SQL ## For Troubleshooting $serverid = $sourceserver.Databases[$InstallDatabase].ExecuteWithResults($sql).Tables.ServerId } catch { Write-Log -path $LogFilePath -message "Failed to Get ServerId from Serverinfo Table - $_" -Level Warn } if ($Port -eq 0 -and $NotContactable -eq $false) { try { # WmiComputer is unreliable :( Use T-SQL $sql = "SELECT local_tcp_port FROM sys.dm_exec_connections WHERE session_id = @@SPID" $Port = $smoserver.ConnectionContext.ExecuteScalar($sql) } catch { Write-Log -path $LogFilePath -message "Port could not be determined for $ServerName. Skipping." -Level Warn Continue } } if ($isclustered -eq $true) { Write-Log -path $LogFilePath -message "$server is clustered - Grabbing Nodes" -Level Info $sql = "Select NodeName FROM sys.dm_os_cluster_nodes" $nodes = $smoserver.ConnectionContext.ExecuteWithResults($sql).Tables.NodeName foreach ($node in $nodes) { if ($SqlInstance -notcontains $node -and $computername -ne $node) { $row = $allservers.Rows | Where-Object { $_.ComputerName -eq $node } if($row.Count -gt 1) { Write-Log -Path $LogFilePath -Message "You appear to have duplicate entries for this ComputerName. Please Check Instance List table and resolve" -Level Error Write-Output "Something went wrong - The Beard is sad :-( . You can find the install log here $($Logfile.FullName)" break } $nodekey = $row.InstanceId if ($null -eq $nodekey) { $nodeupdate = $false } else { $nodeupdate = $true } Write-Log -path $LogFilePath -message "Added clustered node $node to server collection." -Level Info # Populate the datatable try { If ($PSCmdlet.ShouldProcess("Adding $Servername\$InstanceName to datatable")) { $datatable.rows.Add( $nodekey, $serverid, $name, $node, $servername, $InstanceName, $isclustered, $port, 0, $Environment, $Location, $NotContactable, $nodeupdate ) } } catch { Write-Log -path $LogFilePath -message "Failed to add $servername\$InstanceName to the datatable - $_" -Level Error Write-Output "Something went wrong - The Beard is sad :-( . You can find the install log here $($Logfile.FullName)" continue } } } } try { If ($PSCmdlet.ShouldProcess("Adding $Servername\$InstanceName to datatable")) { # Populate the datatable $datatable.rows.Add( $key, $serverid, $name, $computername, $servername, $InstanceName, $isclustered, $port, 0, $Environment, $Location, $NotContactable, $Update ) } } catch { Write-Log -path $LogFilePath -message "Failed to add $servername\$InstanceName to the datatable - $_" -Level Error ## $datatable ## For Troubleshooting Write-Output "Something went wrong - The Beard is sad :-( . You can find the install log here $($Logfile.FullName)" continue } } $rowcount = $datatable.Rows.Count if ($rowcount -eq 0) { Write-Log -path $LogFilePath -message "No rows returned. No update required." -Level Info continue } if ($Update -eq $true) { Write-Log -path $LogFilePath -message "Updating $rowcount row(s)" -Level Info } else { Write-Log -path $LogFilePath -message "Adding $rowcount row(s)" -Level Info } try { If ($PSCmdlet.ShouldProcess("Peforming bulk Insert of $rowcount rows")) { Write-Tvp } } catch { Write-Log -path $LogFilePath -message "Bulk insert failed. Recording exception and quitting.- $_" -Level Error Write-Output "Something went wrong - The Beard is sad :-( . You can find the install log here $($Logfile.FullName)" break } $execaccount = $sourceserver.JobServer.ServiceAccount $testjob = $sourceserver.JobServer.Jobs | Where-Object { $_.Name -like "*dbareports - Test*" } | Select-Object -First 1 $proxy = $testjob.JobSteps[0].ProxyName if ($proxy.length -gt 0) { $proxydetails = $sourceserver.JobServer.ProxyAccounts[$proxy] $proxycredential = $proxydetails.CredentialIdentity $execaccount = "$proxy ($proxycredential)" } $successful = $SqlInstance -join ", " Write-Log -path $LogFilePath -message "Checking to see if $execaccount has access to $successful." -Level Info try { $agentserverjob = $sourceserver.JobServer.Jobs | Where-Object { $_.Name -like "*dbareports - Test Access to Servers and Log Directory" } if ($agentserverjob.CurrentRunStatus -eq "Idle") { If ($PSCmdlet.ShouldProcess("Starting Agent Job $($agentserverjob.name)")) { $agentserverjob.Start() } } do { Start-Sleep -Milliseconds 200 } until ($agentserverjob.CurrentRunStatus -eq "Idle" -or ++$i -eq 20) if ($agentserverjob.LastRunOutcome -eq "Failed") { Write-Log -path $LogFilePath -message "$execaccount cannot access one of the instances in InstanceList. Check dat." -Level Warn } else { Write-Log -path $LogFilePath -message "Lookin' good! $execaccount successfully logged in to $successful." -Level Info } if ($update -ne $true) { Write-Log -path $LogFilePath -message "Populating a few other tables with the info for $successful while we're at it." -Level Info try { Write-Log -path $LogFilePath -message "Starting Agent Job Server job" -Level Info $agentserverjob = $sourceserver.JobServer.Jobs | Where-Object { $_.Name -like "*dbareports - Agent Job Server*" } $agentserverjob.Start() Write-Log -path $LogFilePath -message "Starting Windows Server Information job" -Level Info $winserverInfoJob = $sourceserver.JobServer.Jobs | Where-Object { $_.Name -like "*dbareports - Windows Server Information*" } $winserverInfoJob.Start() Write-Log -path $LogFilePath -message "Starting SQL Server Information job" -Level Info $sqlserverinfojob = $sourceserver.JobServer.Jobs | Where-Object { $_.Name -like "*dbareports - SQL Server Information*" } $sqlserverinfojob.Start() Write-Log -path $LogFilePath -message "Done!" -Level Info } catch { Write-Log -path $LogFilePath -message "Well that didn't go as planned. Please ensure that SQL Server Agent is running on $sqlserver - $_" -Level Warn Write-Output "Something went wrong - The Beard is sad :-( . You can find the install log here $($Logfile.FullName)" } } } catch { Write-Log -path $LogFilePath -message "The Agent Job on $sqlserver cannot contact all servers in the InventoryList. Check job history for details. - $_" Write-Output "Something went wrong - The Beard is sad :-( . You can find the install log here $($Logfile.FullName)" } } END { $sourceserver.ConnectionContext.Disconnect() $title = "Want to review the log?" $message = "Would you like to review the log now? (Y/N)" $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Will continue" $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Will exit" $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) $result = $host.ui.PromptForChoice($title, $message, $options, 0) if ($result -eq 1) { Write-Output "K!" } else { notepad $LogFilePath } } } |