Click here to monitor SSC
SQLServerCentral is supported by Redgate
 
Log in  ::  Register  ::  Not logged in
 
 
 


Write PowerShell Output to SQL Server Table


Write PowerShell Output to SQL Server Table

Author
Message
Jeff Moden
Jeff Moden
SSC-Forever
SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)

Group: General Forum Members
Points: 45329 Visits: 39936
Heh... First time I've had to admit being a "newbie" in a long time so be gentle with me. I might not even know enough to ask the right questions.

I managed to pick up the following PS script from the internet. Lot's of you know what it is. It gets some disk space information from every disk device for the given computer. I've removed all field formatting just to keep it super simple.

Get-WmiObject Win32_LogicalDisk -computer 'SomeComputerName' | Select SystemName,DeviceID,VolumeName,Size,FreeSpace | Format-Table




What I'd like to do is write the output to a table in a given SQL Server. I've Googled for answers and have found some (IMHO) very over complicated methods. Some are generic (which is very cool) but not what I need. I just want to write this one output to a table.

The table for this example is simple as well (I've removed all but the necessary columns including a Date/Time column)...

 CREATE TABLE dbo.DiskSpace
(
SystemName VARCHAR(128),
DeviceID CHAR(2),
VolumeName VARCHAR(128),
Size BIGINT,
FreeSpace BIGINT
)
;




I realize that we're going to need a connection to the database. I'd like it to be a trusted connection so we don't have to hardcode a user name or password. Assume that the server name is "TestServer" and that the database name is "TestDB". From what I've been able to read up on, the business of the connection would look something like this (please correct it if I'm wrong)...

$conn=new-object System.Data.SqlClient.SQLConnection 
$ConnectionString = "Server=TestServer;Database=TestDB;Integrated Security=True;Connect Timeout=0"
$conn.ConnectionString=$ConnectionString
$conn.Open()

...something goes here but I don't know what...

$conn.Close()





Like I said, I'm brand-spanking-new to PowerShell. I sure could use some help on how to get the data from the first script above into the table that I posted the CREATE TABLE code for without having to build a bunch of functions like in the following link.

http://blogs.technet.com/b/heyscriptingguy/archive/2010/11/01/use-powershell-to-collect-server-data-and-write-to-sql.aspx

Thanks for the help, folks. And, yeah... if you have a good book recommendation for the syntax and how to "start thinking in PowerShell" like I do in T-SQL, it would also be much appreciated.

--Jeff Moden

RBAR is pronounced ree-bar and is a Modenism for Row-By-Agonizing-Row.
First step towards the paradigm shift of writing Set Based code:
Stop thinking about what you want to do to a row... think, instead, of what you want to do to a column.
Although they tell us that they want it real bad, our primary goal is to ensure that we dont actually give it to them that way.
Although change is inevitable, change for the better is not.
Just because you can do something in PowerShell, doesnt mean you should. Wink

Helpful Links:
How to post code problems
How to post performance problems
Forum FAQs
Gary Varga
Gary Varga
SSCrazy Eights
SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)

Group: General Forum Members
Points: 8394 Visits: 6168
The simplest way is to build up a string to execute:

$commandText = "INSERT DiskSpace VALUES ('" + $wmiObject.GetValue(0)["SystemName"] + "')"
$command = $conn.CreateCommand()
$command.CommandText = $commandText
$command.ExecuteNonQuery()



This way is not necessarily ideal as it is a little bit verbose, however, I would recommend building up the text as a separate string because you can always output it to the console before using it in anger:

Write-Output $commandText



Just say if you need more or if this is not what you were looking for.

Gaz

-- Stop your grinnin' and drop your linen...they're everywhere!!!
calvo
calvo
Ten Centuries
Ten Centuries (1.4K reputation)Ten Centuries (1.4K reputation)Ten Centuries (1.4K reputation)Ten Centuries (1.4K reputation)Ten Centuries (1.4K reputation)Ten Centuries (1.4K reputation)Ten Centuries (1.4K reputation)Ten Centuries (1.4K reputation)

Group: General Forum Members
Points: 1380 Visits: 3965
Jeff,
I have the very script you're looking for that does exactly what you want.
I pieced it together using a couple functions I found online and it works very well.
-It pulls a list of servers from the text file
-creates temporary data table
-creates function to retrieve data
-executes function which stores data in data table
-imports data table into sql server table
replace values in <> with your variables.


#define servers to be monitored
$server = get-content "<path>.txt"

#data table to hold results
Function out-DataTable
{
$dt = new-object Data.datatable
$First = $true

foreach ($item in $input){
$DR = $DT.NewRow()
$Item.PsObject.get_properties() | foreach {
if ($first) {
$Col = new-object Data.DataColumn
$Col.ColumnName = $_.Name.ToString()
$DT.Columns.Add($Col) }
if ($_.value -eq $null) {
$DR.Item($_.Name) = "[empty]"
}
elseif ($_.IsArray) {
$DR.Item($_.Name) =[string]::Join($_.value ,";")
}
else {
$DR.Item($_.Name) = $_.value
}
}
$DT.Rows.Add($DR)
$First = $false
}

return @(,($dt))

}

#function to retrieve disk information
Function Get-DisksSpace ([string]$Servername, $unit= "GB")
{
$measure = "1$unit"

Get-WmiObject -computername $serverName -query "
select SystemName, Name, DriveType, FileSystem, FreeSpace, Capacity, Label
from Win32_Volume
where DriveType = 2 or DriveType = 3" `
| select @{Label="SystemName";Expression={$serverName.ToUpper()}} `
, Name `
, @{Label="SizeIn$unit";Expression={"{0:n2}" -f($_.Capacity/$measure)}} `
, @{Label="FreeIn$unit";Expression={"{0:n2}" -f($_.freespace/$measure)}} `
, Label
}

#execute the functions
foreach ($s in $server)
{
Get-DisksSpace $s

$dataTable = Get-DisksSpace $s | where {$_.name -like "E:\*" -or $_.name -like "C:\*" -or $_.name -like "F:\*"} | out-DataTable
$connectionString = "Data Source=<server\instance>; Integrated Security=True;Initial Catalog=<databaseName>;"
$bulkCopy = new-object ("Data.SqlClient.SqlBulkCopy") $connectionString
$bulkCopy.DestinationTableName = "<schema.table>"
$bulkCopy.WriteToServer($dataTable)
}



As a side note, I use Win32_Volume instead of LogicalDisk because the volume class returns mount points whereas the logicaldisk class returns only drive letters.

______________________________________________________________________________________________
Forum posting etiquette. Get your answers faster.
Jeff Moden
Jeff Moden
SSC-Forever
SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)

Group: General Forum Members
Points: 45329 Visits: 39936
Gary Varga (6/17/2013)
The simplest way is to build up a string to execute:

$commandText = "INSERT DiskSpace VALUES ('" + $wmiObject.GetValue(0)["SystemName"] + "')"
$command = $conn.CreateCommand()
$command.CommandText = $commandText
$command.ExecuteNonQuery()



This way is not necessarily ideal as it is a little bit verbose, however, I would recommend building up the text as a separate string because you can always output it to the console before using it in anger:

Write-Output $commandText



Just say if you need more or if this is not what you were looking for.


Thanks, Gary.

So would I build the insert for the first two columns like this?

$commandText = "INSERT DiskSpace (SystemName,DeviceID) VALUES ('" + $wmiObject.GetValue(0)["SystemName"] + "," + $wmiObject.GetValue(0)["DeviceID"]+ "')"




Also, there seems to be a bit of hidden magic there. Will 1 insert do it or am I going to have to iterate over each row from the $wmiObject with incrementing values for the operand of .GetValue?

--Jeff Moden

RBAR is pronounced ree-bar and is a Modenism for Row-By-Agonizing-Row.
First step towards the paradigm shift of writing Set Based code:
Stop thinking about what you want to do to a row... think, instead, of what you want to do to a column.
Although they tell us that they want it real bad, our primary goal is to ensure that we dont actually give it to them that way.
Although change is inevitable, change for the better is not.
Just because you can do something in PowerShell, doesnt mean you should. Wink

Helpful Links:
How to post code problems
How to post performance problems
Forum FAQs
Gary Varga
Gary Varga
SSCrazy Eights
SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)

Group: General Forum Members
Points: 8394 Visits: 6168
Almost, unless I am mistaken you have missed single quotes either side of the comma i.e.

$wmiObject.GetValue(0)["SystemName"] + "','" + $wmiObject.GetValue(0)["DeviceID"]+ "')"



I am sure that you will have come across the need for quotes within quotes before. Lovely. ;-)

Gaz

-- Stop your grinnin' and drop your linen...they're everywhere!!!
Jeff Moden
Jeff Moden
SSC-Forever
SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)

Group: General Forum Members
Points: 45329 Visits: 39936
calvo (6/17/2013)
Jeff,
I have the very script you're looking for that does exactly what you want.
I pieced it together using a couple functions I found online and it works very well.
-It pulls a list of servers from the text file
-creates temporary data table
-creates function to retrieve data
-executes function which stores data in data table
-imports data table into sql server table
replace values in <> with your variables.


#define servers to be monitored
$server = get-content "<path>.txt"

#data table to hold results
Function out-DataTable
{
$dt = new-object Data.datatable
$First = $true

foreach ($item in $input){
$DR = $DT.NewRow()
$Item.PsObject.get_properties() | foreach {
if ($first) {
$Col = new-object Data.DataColumn
$Col.ColumnName = $_.Name.ToString()
$DT.Columns.Add($Col) }
if ($_.value -eq $null) {
$DR.Item($_.Name) = "[empty]"
}
elseif ($_.IsArray) {
$DR.Item($_.Name) =[string]::Join($_.value ,";")
}
else {
$DR.Item($_.Name) = $_.value
}
}
$DT.Rows.Add($DR)
$First = $false
}

return @(,($dt))

}

#function to retrieve disk information
Function Get-DisksSpace ([string]$Servername, $unit= "GB")
{
$measure = "1$unit"

Get-WmiObject -computername $serverName -query "
select SystemName, Name, DriveType, FileSystem, FreeSpace, Capacity, Label
from Win32_Volume
where DriveType = 2 or DriveType = 3" `
| select @{Label="SystemName";Expression={$serverName.ToUpper()}} `
, Name `
, @{Label="SizeIn$unit";Expression={"{0:n2}" -f($_.Capacity/$measure)}} `
, @{Label="FreeIn$unit";Expression={"{0:n2}" -f($_.freespace/$measure)}} `
, Label
}

#execute the functions
foreach ($s in $server)
{
Get-DisksSpace $s

$dataTable = Get-DisksSpace $s | where {$_.name -like "E:\*" -or $_.name -like "C:\*" -or $_.name -like "F:\*"} | out-DataTable
$connectionString = "Data Source=<server\instance>; Integrated Security=True;Initial Catalog=<databaseName>;"
$bulkCopy = new-object ("Data.SqlClient.SqlBulkCopy") $connectionString
$bulkCopy.DestinationTableName = "<schema.table>"
$bulkCopy.WriteToServer($dataTable)
}



As a side note, I use Win32_Volume instead of LogicalDisk because the volume class returns mount points whereas the logicaldisk class returns only drive letters.


Thank you, Sir. I was hoping to avoid the out-datable (God bless the "Scripting Guy"!) function because I may have several columns in the table that won't be populated by PowerShell. I just might have to resort to that (can always use a staging table), though, because it seems like the $bulkCopy would probably write to the server more quickly than individual inserts (If that's what Gary's code ends up doing).

--Jeff Moden

RBAR is pronounced ree-bar and is a Modenism for Row-By-Agonizing-Row.
First step towards the paradigm shift of writing Set Based code:
Stop thinking about what you want to do to a row... think, instead, of what you want to do to a column.
Although they tell us that they want it real bad, our primary goal is to ensure that we dont actually give it to them that way.
Although change is inevitable, change for the better is not.
Just because you can do something in PowerShell, doesnt mean you should. Wink

Helpful Links:
How to post code problems
How to post performance problems
Forum FAQs
Jeff Moden
Jeff Moden
SSC-Forever
SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)

Group: General Forum Members
Points: 45329 Visits: 39936
Gary Varga (6/17/2013)
Almost, unless I am mistaken you have missed single quotes either side of the comma i.e.

$wmiObject.GetValue(0)["SystemName"] + "','" + $wmiObject.GetValue(0)["DeviceID"]+ "')"



I am sure that you will have come across the need for quotes within quotes before. Lovely. ;-)


Heh... thanks, Gary. Lovely nested quotes indeed. :-)

The outstanding question is will the code you posted do all of the rows or am I going to have to build 1 insert row for each row found in the $wmiObject?

--Jeff Moden

RBAR is pronounced ree-bar and is a Modenism for Row-By-Agonizing-Row.
First step towards the paradigm shift of writing Set Based code:
Stop thinking about what you want to do to a row... think, instead, of what you want to do to a column.
Although they tell us that they want it real bad, our primary goal is to ensure that we dont actually give it to them that way.
Although change is inevitable, change for the better is not.
Just because you can do something in PowerShell, doesnt mean you should. Wink

Helpful Links:
How to post code problems
How to post performance problems
Forum FAQs
Jeff Moden
Jeff Moden
SSC-Forever
SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)SSC-Forever (45K reputation)

Group: General Forum Members
Points: 45329 Visits: 39936
@Gary,

As a continuation of my previous question, what does the "0" in the parenthesis of .GETVALUE(0) do?

--Jeff Moden

RBAR is pronounced ree-bar and is a Modenism for Row-By-Agonizing-Row.
First step towards the paradigm shift of writing Set Based code:
Stop thinking about what you want to do to a row... think, instead, of what you want to do to a column.
Although they tell us that they want it real bad, our primary goal is to ensure that we dont actually give it to them that way.
Although change is inevitable, change for the better is not.
Just because you can do something in PowerShell, doesnt mean you should. Wink

Helpful Links:
How to post code problems
How to post performance problems
Forum FAQs
Gary Varga
Gary Varga
SSCrazy Eights
SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)

Group: General Forum Members
Points: 8394 Visits: 6168
As for the number of executions you have two choices:

1) Loop through adding multiple inserts to a single command.

$isFirst = $true
$commandText = "INSERT DiskSpace"

$wmiObject = Get-WmiObject Win32_LogicalDisk -computer 'SomeComputerName'
Foreach ($logicalDisk in $wmiObject)
{
if($isFirst) { $isFirst = $false } else { $commandText += "," }
$commandText += " VALUES ('" + $logicalDisk["DeviceID"] + "')"
}

$command = $conn.CreateCommand()
$command.CommandText = $commandText
$command.ExecuteNonQuery()


2) Loop through executing multiple commands.

$wmiObject = Get-WmiObject Win32_LogicalDisk -computer 'SomeComputerName'
Foreach ($logicalDisk in $wmiObject)
{
$commandText = "INSERT DiskSpace VALUES ('" + $logicalDisk["DeviceID"] + "')"
$command = $conn.CreateCommand()
$command.CommandText = $commandText
$command.ExecuteNonQuery()
}



Gaz

-- Stop your grinnin' and drop your linen...they're everywhere!!!
Gary Varga
Gary Varga
SSCrazy Eights
SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)SSCrazy Eights (8.4K reputation)

Group: General Forum Members
Points: 8394 Visits: 6168
Ah yes, good spot:

.GetValue(0)

returns the first logical disk requested. Please see the looping above to see that you can ignore this call now.

Gaz

-- Stop your grinnin' and drop your linen...they're everywhere!!!
Go


Permissions

You can't post new topics.
You can't post topic replies.
You can't post new polls.
You can't post replies to polls.
You can't edit your own topics.
You can't delete your own topics.
You can't edit other topics.
You can't delete other topics.
You can't edit your own posts.
You can't edit other posts.
You can't delete your own posts.
You can't delete other posts.
You can't post events.
You can't edit your own events.
You can't edit other events.
You can't delete your own events.
You can't delete other events.
You can't send private messages.
You can't send emails.
You can read topics.
You can't vote in polls.
You can't upload attachments.
You can download attachments.
You can't post HTML code.
You can't edit HTML code.
You can't post IFCode.
You can't post JavaScript.
You can post emoticons.
You can't post or upload images.

Select a forum

































































































































































SQLServerCentral


Search