I'm trying to modify this script so that it inserts the installed updates into an SQL Server database table.
$conn = New-Object System.Data.SqlClient.SqlConnection
$conn.ConnectionString = "Data Source=sqlserver; Initial Catalog=updates; Integrated Security=SSPI;"
$conn.Open()
$cmd = New-Object System.Data.SqlClient.SqlCommand
$cmd.Connection = $conn
$cmd = $conn.CreateCommand()
$wu = new-object -com “Microsoft.Update.Searcher”
$totalupdates = $wu.GetTotalHistoryCount()
$all = $wu.QueryHistory(0,$totalupdates)
$OutputCollection= #()
Foreach ($update in $all){
$Regex = “KB\d*”
$KB = $string | Select-String -Pattern $regex | Select-Object { $_.Matches }
$output = New-Object -TypeName PSobject
$output | add-member NoteProperty “HotFix ID” -value $KB.‘ $_.Matches ‘.Value
$output | add-member NoteProperty “Title” -value $string
$OutputCollection += $output
$cmd.CommandText += "INSERT INTO dbo.updates (hotfixid, hotfixdescription) VALUES ('$($kb.'$_.Matches'.Value)', ('$($string)'))"
}
$cmd.ExecuteNonQuery()
$conn.close()
At the moment, I'm getting correct number of rows for updates in sql server but it isn't showing hotfixid and in hotfix descriptien columns there is a only one update in all rows.
Thanks!
Do the INSERTs inside the loop. I would, however, recommend that you use prepared statements instead of building the SQL statements via string concatenation. Also, there's no need to build $OutputCollection objects when you're not using it anywhere.
Something like this should work:
...
$wu.QueryHistory(0, $totalupdates) | % {
$KB = $_.Title | ? { $_ -match '(KB\d+)' } | % { $matches[1] }
$cmd = $conn.CreateCommand()
$cmd.CommandText = "INSERT INTO dbo.updates (hotfixid, hotfixdescription) " +
"VALUES (#id, #descr)"
$cmd.Parameters.AddWithValue("#id", $KB)
$cmd.Parameters.AddWithValue("#descr", $_.Title)
$cmd.Prepare()
$cmd.ExecuteNonQuery()
}
...
Untested, though, since I don't have an SQL Server at hand. I also suspect that there's a more efficient way to handle the prepared statements, but I'm not that familiar with SQL Server.
Related
So I'm writing a powershell script and utilizing the Module "sqlserver" to use the "Read-SqlTableData" cmdlet. I see there is a -topN X filter which selects from the top of the datatable but the way our table is set up, the oldest records at at the top but im trying to get newer records. Is there a way to select the last X records from the bottom?
This is what i have now. Any help is appreciated!
$SQLData=Read-SqlTableData -ServerInstance "Server" -Database "Database" -SchemaName "schema" -tablename "table" -ColumnName "column","column2","column3"
I do it like this, run a query and get the exact data you need.
try
{
$ConnectionString = "Server=$($DatabaseServer);Integrated Security=true;Initial Catalog=$($DatabaseName)"
$Query = "SELECT TOP $($BatchSize) * FROM [$($DatabaseName)].[dbo].[$($TableName)] WHERE $($colStatus) = 30"
Write-Host "DEBUG: `$Query is $Query " -ForegroundColor DarkYellow
$sqlConn = New-Object System.Data.SqlClient.SqlConnection
$sqlConn.ConnectionString = $ConnectionString
$sqlConn.Open()
$sqlcmd = New-Object System.Data.SqlClient.SqlCommand
$sqlcmd.Connection = $sqlConn
$sqlcmd.CommandText = $Query
$adp = New-Object System.Data.SqlClient.SqlDataAdapter $sqlcmd
$tbldata = New-Object System.Data.DataSet
$adp.Fill($tbldata) | Out-Null
try {$sqlConn.Close()} catch {}
}
catch
{
$errorMsg = "ERROR: Cannot connect to Database $DatabaseName using the following Connection String $ConnectionString. Query is $Query. Get-KLMigrationSQLData."
}
if ($tbldata)
{
Write-Host "DEBUG: Creating `$VDB Global Variable from Database Table:
$TableName" -ForegroundColor DarkYellow
$dataFill = New-Variable -Name "VDB" -Force -PassThru -Scope Global
$dataFill.Value = #()
foreach ($DBRow in $tbldata.Tables.Rows)
{
$tempObj = New-Object System.Object
$tempObj | Add-Member -Type NoteProperty -Name TableName -Value $TableName
$DBRows = $DBRow | Get-Member | Where-Object {$_.MemberType -eq 'Property' -or $_.MemberType -eq 'ParameterizedProperty'}
ForEach ($Row in $DBRows){
if ($Row.Name -ne 'Item')
{
$colName = $Row.Name
$colValue = $DBRow.Item($colName)
$tempObj | Add-Member -Type NoteProperty -Name $colName -Value $colValue
}
}
$dataFill.Value += $tempObj
}
}
I have a basic SQL table of Employees. Using a powershell script I want to export all the employees who have made over 1000 sales to a .csv file and the rest into a different .csv file. I want to accomplish this task by using a loop. I am new to powershell and want to learn the foundations. Can anyone help?
SQL Table (not real employees)
This is what I have so far:
$connection.Open()
[System.Data.SqlClient.SqlDataReader]$result = $cmd.ExecuteReader()
$highDestFile = "C:\high-sales.csv"
$lowDestFile = "C:\low-sales.csv"
while($result.Read()) {
$ename = $result.GetValue(3);
$job = $result.GetValue(4);
$sales = $result.GetValue(7);
$tableArray = New-Object System.Collections.ArrayList
$tableArray.Add($ename)
$tableArray.Add($job)
$tableArray.Add($sales)
if($sales -ge 1000) {
Out-File -FilePath $highDestFile -InputObject $tableArray -Encoding ASCII -Append
} else {
Out-File -FilePath $lowDestFile -InputObject $tableArray -Encoding ASCII -Append
}
}
$connection.Close()
I'm not real familiar with the method you're using to get your results, but I think I have something similar that might be easier to work with for you. It will get all results into PS, and you can filter things from there, rather than getting one result at a time. You obviously know how to make your own SqlConnection and SqlCommand, I'm just including them for future readers to reference.
# Define SQL query
$sqlQuery = #"
SELECT *
FROM MyTable
"#
# Create a SqlConnection to connect to the SQL DB
$sqlConnection = New-Object System.Data.SqlClient.SqlConnection
$sqlConnection.ConnectionString = "Server = $SqlServer; Database =$SqlCatalog; User Id = $User; Password = $Password"
# Create a SqlCommand object to define the query
$sqlCmd = New-Object System.Data.SqlClient.SqlCommand
$sqlCmd.CommandText = $sqlQuery
$sqlCmd.Connection = $sqlConnection
# Create a SqlAdapter that actually does the work and manages everything
$sqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$sqlAdapter.SelectCommand = $sqlCmd
# Create an empty DataSet for the query to fill with its results
$dataSet = New-Object System.Data.DataSet
# Execute the query and fill the DataSet (then disconnect)
$sqlAdapter.Fill($dataSet)
$sqlConnection.Close()
# Convert DataSet table to array for ease of use
[Array]$Results = $dataSet.Tables[0]
Beyond that you could just use a pair of Where statements to filter your results, and output to files.
$Results | ?{[int]$_.Sales -ge 1000} | Set-Content $highDestFile
$Results | ?{[int]$_.Sales -lt 1000} | Set-Content $lowDestFile
Here is the function I have setup that works just fine to send queries to a SQL database from PowerShell and return the results (the results are what I don't quite understand)
function Invoke-SQL
{
param (
[string]$server,
[string]$database,
[string]$Query
)
$connectionString = "Data Source=$server; " +
"Integrated Security=SSPI; " +
"Initial Catalog=$database"
$connection = new-object
system.data.SqlClient.SQLConnection($connectionString)
$command = new-object system.data.sqlclient.sqlcommand($Query, $connection)
$connection.Open()
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataSet) | Out-Null
$connection.Close()
$dataSet.Tables
}
If I run a query such as the one below (it returns no results, meaning there were no records that existed that matched the condition) why does it return nothing when I just put in $results? Why is the result 'Table' when I do Write-Host $results ? See below
PS>$results = Invoke-SQL -server 'servername' -database 'DBname' -Query "SELECT * FROM [DBname].[dbo].[TableName] WHERE UserID = 'x' AND ComputerName = 'x'"
PS>$results
PS>Write-Host $results
Table
When no records are found I thought it would be equal to "" or $null but it is not upon testing
$null test
PS>If ($results -eq $null) {
>> write-host "Null"}else{
>> write-host "Not Null"
>> }
Not Null
"" test
PS>If ($results -eq "") {
>> write-host "Empty"}else{
>> write-host "Not Empty"
>> }
If someone could explain this to me, and what options I might have in order to check if a query returns no results, that would be great!
Read the comments on the question post for more details.
In order to see if records were returned or not, this will return the number of rows (records) returned. Credit to #Bill_Stewart.
($results | Measure-Object).Count
#Tomalak provided a helpful link.
#BaconBits had this helpful tip to get the type of an object
$results.GetType().FullName
# or
$results | Get-Member
Thank you all for your help.
I'm struggling to get my head around this one, I've only just begun looking at scripting in SQL, and my powershell is very limited. The requirments are basically this:
Utilisng Powershell, import a csv file which contains one column that needs to feed into multiple SQL queries via a loop, exporting a seperate csv file for each different query.
example import of csv:
Project (heading)
1000
1001
1002
Powershell:
$importProjectsCSV = e:\Projects.csv
$server = servername
$database = database
import-csv $importProjectsCSV | ForEach-Object {
$query = "
Select ProjectLeader, ProjectTitle
FROM dbo.PROJECTS
Where Project = $_.Project;
Select ProjectClient, Name
FROM dbo.CLIENTS
Where Project = $_.Project;
$connectionTemplate = "Data Source={0};Integrated Security=SSPI;Initial Catalog={1};"
$connectionString = [string]::Format($connectionTemplate, $server, $database)
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$command = New-Object System.Data.SqlClient.SqlCommand
$command.CommandText = $query
$command.Connection = $connection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $command
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$connection.Close()
$dataset.Table[0] | Export-csv "E:\" + $_.ProjectName + ".csv"
$dataset.Table[1] | Export-csv "E:\" + $_.ProjectName + ".csv"
The problem is that the variable isn't coming into the SQL query.
Is there a better way to handle this type of example?
Appreciate any pointers
Paul.
I would do something like this:
I must admit i havent been able to test it, and personally i usually use c# to query sql servers. So i might have gone a bit wrong somewhere.
$importProjectsCSV = e:\Projects.csv
$server = servername
$database = database
$Projects = Import-Csv -Path $importProjectsCSV | % {$_.Project}
$DS_Projects = New-Object System.Data.DataSet
$DS_Clients = New-Object System.Data.DataSet
$query_pro = "Select ProjectLeader, ProjectTitle, Project FROM dbo.PROJECTS";
$query_Clients = "Select ProjectClient, Name, Project FROM dbo.CLIENTS";
$connectionTemplate = "Data Source={0};Integrated Security=SSPI;Initial Catalog={1};"
$connectionString = [string]::Format($connectionTemplate, $server, $database)
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$connection.Open()
$command = $connection.CreateCommand();
$command.CommandText = $query_pro;
$sqlAdap = New-Object System.Data.SqlClient.SqlDataAdapter($command)
$sqlAdap.Fill($DS_Projects)
$command2 = $connection.CreateCommand();
$command2.CommandText = $query_Clients;
$sqlAdap2 = New-Object System.Data.SqlClient.SqlDataAdapter($command2)
$sqlAdap2.Fill($DS_Clients)
$connection.Close();
foreach($project in $Projects)
{
$DS_Projects.Tables[0].Select("Project=$project") | Export-Csv "E:\$project.csv"
$DS_Clients.Tables[0].Select("Project=$project") | Export-Csv "E:\$project.csv"
}
If I get every right you could do this with something like this:
$Projects = Import-Csv -Path 'C:\Temp\Projects.csv'
ForEach ($Project in $Projects.Projects) {
$Query= #"
Select ProjectLeader, ProjectTitle
FROM dbo.PROJECTS
Where Project = '%{0}';
"# -f $Project
# just dummy action
$results = Invoke-Sqlcmd -ServerInstance "foo" -Database "bar" -Query $Query
# In Case results is a dataset do something like
$results | Export-Csv -Path ("E:\{0}.csv" -f $Project )
}
First off, I'm new to stack. I have referenced stack many times in the past, but recently I have been stuck on this issue for quite sometime. So here goes.
My goal:
I am attempting to correlate an array output from VMware that matches a custom value on each VM machine. ( an asset ID ) to a value ( ID Key ) on a microsoft SQL 2000 server.
As such, since this server is pre 2005 I am unable to use the invoke-sqlcmd powershell command. I have to utilize the full SQL connection string and command structure to return a value out of this database. This sql statement and script works fine on its own. Meaning that the sql portion of this script, functioning on its own will pull results out of the database with a manual tag number put in place of my variable "$etag". I'm fairly new to powershell, and sql use from powershell.
So here is my script with names of the protected taken out.
#========================================================================
# Created on: 12/4/2013 2:01 PM
# Created by: Shaun Belcher
# Filename:
#========================================================================
function get-inventory
{
Add-PSSnapin VMware.VimAutomation.Core
$date=get-date
$vcenterserver = #("srv-1","srv-2","srv-3")
Connect-VIServer -server $vcenterserver
$toAddr="user#domain.com"
$fromAddr="user#domain.com"
$smtpsrv="mail.domain.com"
#Variables
$mdesks=#()
$sqlServer = "serverdb"
$sqlDBNAME = "instance"
$sqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$sqlConnection = New-Object System.Data.SqlClient.SqlConnection
$DataSet = New-Object System.Data.DataSet
$sqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.connection = $sqlConnection
$sqlAdapter.SelectCommand = $sqlCmd
#db Connection
$sqlConnection.ConnectionString = "Server = $sqlServer; Database = $sqlDBname; Integrated Security=True;"
$SqlCmd.connection = $SqlConnection
$SqlCmd.commandtext = $sqlQuery
$sqlAdapter.SelectCommand = $sqlCmd
$sqlQuery += "SELECT INVHARDW_PropTag as proptag, invhardw_clientID as ClientID, invhardw_notes as Notes FROM INV_Hardware where invhardw_proptag = '$etag';"
$SqlCmd.commandtext = $sqlQuery
$sqlAdapter.SelectCommand = $sqlCmd
$sqlAdapter.Fill($DataSet)
$DataSet.Tables[0]
$sqlConnection.Close()
$mdesks = #($DataSet.Tables[0] | select propTag, ClientID, Notes)
$virtuals= #(Get-VM | select Name,vmhost,memoryMB,#{N="Datastore";E={[string]::Join(',',(Get-Datastore -Id $_.DatastoreIdList | Select -ExpandProperty Name))}})
$etags = #(Get-vm | Get-Annotation |select value,#{N="mDeskNote";E={[string]::Join(',',($mdesk | Where-Object {$mdesks.propTag = $_;}))}},#{N="mDeskClientID";E={[string]::Join(',',($mdesk | Where-Object {$mdesks.propTag = $_;}))}})
if($virtuals -ne $null){
$body = #("
<center><table border=1 width=50 % cellspacing=0 cellpadding=8 bgcolor=Black cols=3>
<tr bgcolor=White><td>Virtual Machine</td><td>Host Machine</td><td>Memory Allocated</td><td>DatastoreList</td><td>Asset Tag</td><td>App Note</td><td>App Client ID</td></tr>")
$i = 0
do {
#if($i % 2){$body += "<tr bgcolor=#D2CFCF><td>$($virtuals[$i].Name)</td></tr>";$i++}
#else {$body += "<tr bgcolor=#EFEFEF><td>$($virtuals[$i].Name)</td></tr>";$i++}
if($i % 2){$body += "<tr bgcolor=#D2CFCF><td>$($virtuals[$i].Name)</td><td>$($virtuals[$i].VMHost)</td><td>$($virtuals[$i].MemorymB)</td><td>$($virtuals[$i].datastore)</td><td>$($etags[$i].value)</td><td>$mdesks[$i].notes</td><td>$mdesks[$i].ClientID</td></tr>";$i++}
else {$body += "<tr bgcolor=#EFEFEF><td>$($virtuals[$i].Name)</td><td>$($virtuals[$i].VMHost)</td><td>$($virtuals[$i].memorymb)</td><td>$($virtuals[$i].datastore)</td><td>$($etags[$i].value)</td><td>$mdesks[$i].notes</td><td>$mdesks[$i].ClientID</td></tr>";$i++}
}
while ($virtuals[$i] -ne $null)
$body += "</table></center>"
# Send email.
if($attachmentPref){
$virtuals | Export-CSV "Inventory $($date.month)-$($date.day)-$($date.year).csv"
Send-MailMessage -To "$toAddr" -From "$fromAddr" -Subject "$vcenterserver Inventory = $countvms" -Body "$body" -Attachments "Inventory $($date.month)-$($date.day)-$($date.year).csv" -SmtpServer "$smtpsrv" -BodyAsHtml
Remove-Item "Inventory $($date.month)-$($date.day)-$($date.year).csv"
}
Else{
Send-MailMessage -To "$toAddr" -From "$fromAddr" -Subject "Inventory $vcenterserver = $countvms" -Body "$body" -SmtpServer "$smtpsrv" -BodyAsHtml
}
}
Disconnect-VIServer -Server $vcenterserver -Confirm:$false exit
get-inventory
This returns the information and sends it in an email with columns and rows of the information. Again, these are two working scripts that just do not return the result that is sought after.