Click here to monitor SSC
SQLServerCentral is supported by Red Gate Software Ltd.
 
Log in  ::  Register  ::  Not logged in
 
 
 
        
Home       Members    Calendar    Who's On


Add to briefcase 1234»»»

Write PowerShell Output to SQL Server Table Expand / Collapse
Author
Message
Posted Saturday, June 15, 2013 10:40 PM


SSC-Dedicated

SSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-Dedicated

Group: General Forum Members
Last Login: Today @ 12:42 PM
Points: 35,216, Visits: 31,672
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."

(play on words) "Just because you CAN do something in T-SQL, doesn't mean you SHOULDN'T." --22 Aug 2013

Helpful Links:
How to post code problems
How to post performance problems
Post #1463926
Posted Monday, June 17, 2013 6:56 AM


SSCertifiable

SSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiable

Group: General Forum Members
Last Login: Today @ 12:12 PM
Points: 5,433, Visits: 3,176
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!!!
Post #1464122
Posted Monday, June 17, 2013 7:42 AM
Ten Centuries

Ten CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen Centuries

Group: General Forum Members
Last Login: Monday, August 18, 2014 9:20 AM
Points: 1,259, Visits: 3,567
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.
Post #1464146
Posted Monday, June 17, 2013 8:26 AM


SSC-Dedicated

SSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-Dedicated

Group: General Forum Members
Last Login: Today @ 12:42 PM
Points: 35,216, Visits: 31,672
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."

(play on words) "Just because you CAN do something in T-SQL, doesn't mean you SHOULDN'T." --22 Aug 2013

Helpful Links:
How to post code problems
How to post performance problems
Post #1464171
Posted Monday, June 17, 2013 8:32 AM


SSCertifiable

SSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiable

Group: General Forum Members
Last Login: Today @ 12:12 PM
Points: 5,433, Visits: 3,176
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!!!
Post #1464175
Posted Monday, June 17, 2013 8:35 AM


SSC-Dedicated

SSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-Dedicated

Group: General Forum Members
Last Login: Today @ 12:42 PM
Points: 35,216, Visits: 31,672
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."

(play on words) "Just because you CAN do something in T-SQL, doesn't mean you SHOULDN'T." --22 Aug 2013

Helpful Links:
How to post code problems
How to post performance problems
Post #1464178
Posted Monday, June 17, 2013 8:38 AM


SSC-Dedicated

SSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-Dedicated

Group: General Forum Members
Last Login: Today @ 12:42 PM
Points: 35,216, Visits: 31,672
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."

(play on words) "Just because you CAN do something in T-SQL, doesn't mean you SHOULDN'T." --22 Aug 2013

Helpful Links:
How to post code problems
How to post performance problems
Post #1464183
Posted Monday, June 17, 2013 8:45 AM


SSC-Dedicated

SSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-Dedicated

Group: General Forum Members
Last Login: Today @ 12:42 PM
Points: 35,216, Visits: 31,672
@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."

(play on words) "Just because you CAN do something in T-SQL, doesn't mean you SHOULDN'T." --22 Aug 2013

Helpful Links:
How to post code problems
How to post performance problems
Post #1464188
Posted Monday, June 17, 2013 9:24 AM


SSCertifiable

SSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiable

Group: General Forum Members
Last Login: Today @ 12:12 PM
Points: 5,433, Visits: 3,176
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!!!
Post #1464209
Posted Monday, June 17, 2013 9:26 AM


SSCertifiable

SSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiable

Group: General Forum Members
Last Login: Today @ 12:12 PM
Points: 5,433, Visits: 3,176
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!!!
Post #1464211
« Prev Topic | Next Topic »

Add to briefcase 1234»»»

Permissions Expand / Collapse