Access datarow from a workflow - powershell

I have a script which look like below:
function Invoke-SQL {
param(
[string] $dataSource = ".\MSSQLSERVER",
[string] $database = "master",
[string] $sqlCommand = $(throw "Please specify a query.")
)
$connectionString = "Data Source=$dataSource; Integrated Security=True; Initial Catalog=$database; Connect Timeout=100"
$connection = new-object system.data.SqlClient.SQLConnection($connectionString)
$command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)
Try {
$connection.Open()
} catch {
return, $null
}
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataSet) | Out-Null
$connection.Close()
$dataSet.Tables
}
[string]$SQL = "select CmsSrvName from CmsServerList_VM"
$DbInstances = Invoke-SQL "DBSRV" "Test" $SQL
workflow wf1{
Param([System.Data.DataTable]$instance)
ForEach -Parallel -ThrottleLimit 10 ($i in $instance) {
InlineScript{
$t = $using:i
Write-Verbose "$t['CmsSrvName']"
}
}
}
wf1 -Verbose $DbInstances
Output:
VERBOSE: [localhost]:System.Data.DataRow['CmsSrvName']
VERBOSE: [localhost]:System.Data.DataRow['CmsSrvName']
VERBOSE: [localhost]:System.Data.DataRow['CmsSrvName']
The output is not what I expected, it just print out the type name not the value. How can I access the DataRow value in a workflow?(in Powershell 5)
Thanks in advance for the help

Related

Powershell Gui not returning SQL query results

I'm building a simple GUI where support staff can look up basic user information. The GUI is supposed to pull this data from an SQL DB, but nothing happens when the button is pressed.
function SQLquery {
param (
[Parameter(Mandatory = $true)]
[ValidateSet('NemOrgDB')]
[string]
$System,
[string]
$Query
)
switch ($System) {
NemOrgDB { $ConnectionString = 'Place holder for Stack Overflow' }
}
#New SqlConnection object
$sqlConnection = New-Object System.Data.SqlClient.SqlConnection
$sqlConnection.ConnectionString = $ConnectionString
$sqlConnection.Open()
#New Sqlcommand object
$sqlCommand = New-Object System.Data.SqlClient.SqlCommand
$sqlCommand.Connection = $sqlConnection
$sqlCommand.CommandText = $Query
#Create new sqldataadapter object
$sqlDataAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$sqlDataAdapter.SelectCommand = $sqlCommand
#Create new dataset object
$DataSet = New-Object System.Data.DataSet
#Fill dataset with dataadapter input
try {
$sqlDataAdapter.Fill($DataSet) | Out-Null
$output = $DataSet.Tables
}
catch {
$output = $null
}
#Close sql connection
$sqlConnection.close()
$null = $sqlConnection
$output
}
Here is the Function in use:
#Søgnigns criteria
$EmployeeID = Read-Host -Prompt "What user to find?"
#SQL Query
SQLquery -System NemOrgDB -Query "Select [medarbejder_wnr]
,[navn]
,[stilling]
,[stilling_nr]
,[firmakode_txt]
FROM [PersonData_NemOrg].[dbo].[Personale]
WHERE medarbejder_wnr ='$EmployeeID';"
The 2 pieces of code work as they should in a console environment with no Gui attached, they return the following results:
EmployeeID: EmployeeID
Name: Name
Role: IT support
Role number : Number
fimakode_txt : Company
When the same code is run inside the GUI nothing happens, here is the GUI code:
#ButtonSearch
#
$ButtonSearch.BackColor = [System.Drawing.SystemColors]::ActiveBorder
$ButtonSearch.FlatStyle = [System.Windows.Forms.FlatStyle]::System
$ButtonSearch.Font = (New-Object -TypeName System.Drawing.Font -ArgumentList #([System.String]'Tahoma',[System.Single]9,[System.Drawing.FontStyle]::Bold,[System.Drawing.GraphicsUnit]::Point,([System.Byte][System.Byte]0)))
$ButtonSearch.Location = (New-Object -TypeName System.Drawing.Point -ArgumentList #([System.Int32]12,[System.Int32]118))
$ButtonSearch.Name = [System.String]'ButtonSearch'
$ButtonSearch.Size = (New-Object -TypeName System.Drawing.Size -ArgumentList #([System.Int32]150,[System.Int32]23))
$ButtonSearch.TabIndex = [System.Int32]24
$ButtonSearch.Text = [System.String]'Search'
$ButtonSearch.UseCompatibleTextRendering = $true
$ButtonSearch.UseVisualStyleBackColor = $false
$ButtonSearch.add_Click($ButtonSearch_Click)
#
AND
$ButtonSearch_Click = {
$Wnummer = $TextBoxWnummer1.Text
SQLquery -System NemOrgDB -Query "Select [medarbejder_wnr]
,[navn]
,[stilling]
,[stilling_nr]
,[firmakode_txt]
FROM [PersonData_NemOrg].[dbo].[Personale]
WHERE medarbejder_wnr ='$Wnummer';"
}
It simply returns nothing.

Compound Dynamic Parameters in Powershell

I am attempting to use two Dynamic Parameters in the same cmdlet. The problem is that I would like the second dynamic parameter to use the first parameter to populate the result set.
For example the syntax for using the command could be something like this -
Get-MSRP -Manufacturer Jeep -Trim Rubicon
'Manufacturer' is a dynamic parameter that looks at a file on the disk to populate values for the user. I would 'Trim' to consume the 'Manufacturer' option that the user chose to create a result set of 'Trim'.
My 'Manufacturer' is working correctly but I believe that the value which the user has chosen isn't available when the code for the DynamicParam I've created for 'Trim', has been ran.
Any help?
function Get-MSRP{
[cmdletbinding()]
param()
DynamicParam{
$Param1 = "Manufacturer",0,{GC c:\temp\manufacturers.txt},$False
$Param2 = "Trim",1,{GC c:\temp\$Manufacturer\Trim.txt},$False
Get-DynamicParameterSet $Param1,$Param2
}
begin{
$Manufacturer = $PSBoundParameters["Manufacturer"]
$Trim = $PSBoundParameters["Trim"]
}
process{
return Invoke-Sqlcmd -Database 'db' -ServerInstance 'server' -Query
"Select MSRP from pricing.MSRP where Manufacturer = '$Manufacturer'
and Trim = '$Trim'"
}
}
function Get-DynamicParameterSet{
param($Params)
$RuntimeParameterDictionary = New-Object
System.Management.Automation.RuntimeDefinedParameterDictionary
if($Params[0].GetType().Name -eq "String" ){
Write-Debug "Single Param to build"
$RuntimeParameterDictionary = BuildSet $Params[0] $Params[1]
$([Scriptblock]$Params[2]) $Params[3] $Params[4] $RuntimeParameterDictionary
}else{
foreach($Param in $Params){
$RuntimeParameterDictionary = BuildSet $Param[0] $Param[1]
$([Scriptblock]$Param[2]) $Param[3] $Param[4]
$RuntimeParameterDictionary
}
}
return $RuntimeParameterDictionary
}
function BuildSet{
param(
$ParameterName,
[int]$Position,
[ScriptBlock]$scriptBlock,
$Mandatory,
$SetNames,
[System.Management.Automation.RuntimeDefinedParameterDictionary]$RuntimeParameterDictionary)
Write-Debug "Setting up $ParameterName"
$AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.Mandatory = $Mandatory
$ParameterAttribute.Position = $Position
$ParameterAttribute.ParameterSetName = $SetNames
$AttributeCollection.Add($ParameterAttribute)
Write-Debug "Generating result set"
$arrSet = Invoke-Command $scriptBlock
Write-Debug "Generated as $arrSet"
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
$AttributeCollection.Add($ValidateSetAttribute)
$RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection)
$RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter)
return $RuntimeParameterDictionary
}
Minimal & Verifiable
function Get-MSRP{
[cmdletbinding()]
param()
DynamicParam{
$Param1 = "Manufacturer",0,{GC c:\temp\manufacturers.txt},$False
$Param2 = "Trim",1,{gci c:\temp\$Manufacturer},$False
Get-DynamicParameterSet $Param1,$Param2
}
begin{
$Manufacturer = $PSBoundParameters["Manufacturer"]
$Trim = $PSBoundParameters["Trim"]
}
process{
return Invoke-Sqlcmd -Database 'db' -ServerInstance 'server' -Query
"Select MSRP from pricing.MSRP where Manufacturer = '$Manufacturer'
and Trim = '$Trim'"
}
}
function Get-DynamicParameterSet{
param($Params)
$RuntimeParameterDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
foreach($Param in $Params){
$ParameterName = $Param[0]
[int]$Position = $Param[1]
[ScriptBlock]$scriptBlock = $([Scriptblock]$Param[2])
$Mandatory = $Param[3]
$SetNames =$Param[4]
$AttributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new()
$ParameterAttribute = [System.Management.Automation.ParameterAttribute]::new()
$ParameterAttribute.Mandatory = $Mandatory
$ParameterAttribute.Position = $Position
$ParameterAttribute.ParameterSetName = $SetNames
$AttributeCollection.Add($ParameterAttribute)
$arrSet = Invoke-Command $scriptBlock
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
$AttributeCollection.Add($ValidateSetAttribute)
$RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection)
$RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter)
}
return $RuntimeParameterDictionary
}
After some research, i've found that you cannot have this type of 'Compound dynamic parameter' in powershell where the second dynamic parameter uses a variable output from the users first dynamic parameter. The reason being that the values aren't bound to dynamic parameter until the command is ran.
Another note on Dynamic parameters, the ValueFromPipeline order is not respected when using them!

How do I pass parameters properly?

Please see my Function-to-be below:
Function Query {
param (
[string]$query
[string]$server
[string]$dbase
[string]$user
[string]$pass
)
if ($user) {
$connstr = "Server={0};Database={1};User ID={2};Password={3};Trusted_Connection=False;Connect Timeout=15" -f $server, $dbase, $user, $pass
}
else {
$connstr = "Server={0};Database={1};Integrated Security=True;Connect Timeout=15" -f $server, $dbase
}
$conn.ConnectionString = $connstr
switch ($query.Split()[0]) {
"SELECT" {
$cmd = New-Object System.Data.SqlClient.SqlCommand($query,$conn)
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter($cmd)
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataset) | Out-Null
return $dataset
}
"UPDATE" {
$cmd = New-Object System.Data.SqlClient.SqlCommand($query,$conn)
return $cmd.ExecuteNonQuery()
}
"INSERT" {
$cmd = New-Object System.Data.SqlClient.SqlCommand($query,$conn)
return $cmd.ExecuteNonQuery()
}
}
}
Query -query "SELECT TOP 10 myField FROM myTable" -server "SQLEXPRESS" -dbase "TEST"
This doesn't work, Powershell ISE gives me red in the param section but I don't understand why. Because of the many different ways people seem to construct Powershell functions (I'm a beginner :)) I am somewhat confused.
How do I make this function work?
Put commas , between the parameters:
Function SomeName {
Param ($param1,$param2,$param3)
}
etc. You can use whitespace or line break after the comma for readability, which is what most people do:
Function SomeName {
param (
$param1,
$param2
)
}

PowerShell Converting $null into 0

I have a pretty straight-forward function for grabbing SQL results:
function RunSqlCommand($sql)
{
$connection = $null
$command = $null
try
{
$connectionString = "data source=localhost; integrated security=true"
$connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)
$command = $connection.CreateCommand()
$command.CommandText = $sql
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter($command)
$dataSet = New-Object System.Data.DataSet
$adapter.Fill($dataSet)
$results = $dataSet.Tables | Select-Object -ExpandProperty "Rows"
return $results
}
finally
{
if ($command -ne $null) { $command.Dispose() }
if ($connection -ne $null) { $connection.Dispose() }
}
}
Whenever there are no results, the $results variable is $null. However, when I inspect the return value in the calling method, it magically becomes 0.
Is PowerShell doing something behind the scenes? I really do want to return $null to represent "no results".
$adapter.Fill() returns the number of rows added or refreshed in the dataset.
To fix, you can do this:
[void]$adapter.Fill($dataSet)
or
$adapter.Fill($dataset) | out-null

Writing Powershell Error messages to SSIS

I have an SSIS execute process task where I am calling a PowerShell script. I found that I can get the process task to fail if the PowerShell script fails by trapping the errors like this:
trap {
$err = $_.Exception
while ( $err.InnerException )
{
$err = $err.InnerException
Write-Host $err.Message
};
exit 1
}
I would like to get the error messages to return to SSIS, but I can't seem to get it to work. It just returns the following:
The process exit code was "1" while the expected was "0".
Any help is appreciated.
Full Code:
Param(
[String]$excelPath,
[String]$serverName,
[String]$databaseName,
[String]$tableName
)
$ErrorActionPreference = 'Stop'
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO.SqlDataType') | Out-Null
Trap {
$err = $_.Exception
while ( $err.InnerException )
{
$err = $err.InnerException
Write-Host $err.Message
};
exit 1
}
$excel = New-Object -ComObject excel.application
$excel.visible = $False
$excel.displayalerts=$False
$workbook = $excel.Workbooks.Open($ExcelPath)
$workSheet = $workbook.worksheets.Item(1).name
$workbook.Close()
$excel.quit()
$excel = $null
$query = "select * from [$workSheet`$]";
$connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=`"$excelPath`";Extended Properties=`"Excel 12.0 Xml;HDR=YES`";"
# Instantiate some objects which will be needed
$serverSMO = New-Object Microsoft.SqlServer.Management.Smo.Server($serverName)
$db = $serverSMO.Databases[$databaseName];
$newTable = New-Object Microsoft.SqlServer.Management.Smo.Table ;
$newTable.Parent = $db;
$newTable.Name = $tableName ;
$conn = New-Object System.Data.OleDb.OleDbConnection($connectionString)
$conn.open()
$cmd = New-Object System.Data.OleDb.OleDbCommand($query,$conn)
$dataAdapter = New-Object System.Data.OleDb.OleDbDataAdapter($cmd)
$dataTable = New-Object System.Data.DataTable
$dataAdapter.fill($dataTable)
$conn.close()
# Drop the table if it exists
if($db.Tables.Contains($tableName).Equals($true))
{
($db.Tables[$tableName]).Drop()
}
# Iterate the columns in the DataTable object and add dynamically named columns to the SqlServer Table object.
foreach($col in $dataTable.Columns)
{
$sqlDataType = [Microsoft.SqlServer.Management.Smo.SqlDataType]::Varchar
$dataType = New-Object Microsoft.SqlServer.Management.Smo.DataType($sqlDataType);
$dataType.MaximumLength = 1000;
$newColumn = New-Object Microsoft.SqlServer.Management.Smo.Column($newTable,$col.ColumnName,$dataType);
$newColumn.DataType = $dataType;
$newTable.Columns.Add($newColumn);
}
$newTable.Create();
#bcp data into new table
$connectionString = "Data Source=$serverName;Integrated Security=true;Initial Catalog=$databaseName;"
$bc = New-Object ("Data.SqlClient.SqlBulkCopy") $connectionString
$bc.DestinationTableName = "$tableName"
$bc.WriteToServer($dataTable)