Working with System.ComponentModel.MarshalByValueComponent - powershell

I'm pulling data from SQL using System.Data.SqlClient and eventaully end up with a dataset stored into a variable:
$SqlQuery = "select * from my_Table"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $SQLDBName; Integrated Security = True"
$SqlConnection.Open()
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$amManagedFolders = New-Object System.Data.DataSet
$SqlAdapter.Fill($dataset)
I then access the table via $dataset.Tables[0] which is of type: System.ComponentModel.MarshalByValueComponent
I need to be able to use ForEach in order to loop through that table and store the results into a CustomPSObject for later use, however, I am not sure of a method to use in order to achieve this.

Using this page I found the following code helpful. I'm taking a SQL query and producing an Excel workbook with the results.
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
$DataSetTable = $DataSet.Tables["Table"]
## - Build the Excel column heading:
[Array] $getColumnNames = $DataSetTable.Columns | Select ColumnName
## - Build column header:
[Int] $RowHeader = 1
foreach ($ColH in $getColumnNames)
....
foreach ($rec in $DataSetTable.Rows)
{
foreach ($Coln in $getColumnNames)
{
## - Next line convert cell to be text only:
$xlsSh.Cells.NumberFormat = "#"
## - Populating columns:
$xlsSh.Cells.Item($rowData, $colData) = $rec.$($Coln.ColumnName).ToString()
$ColData++
}
$rowData++; $ColData = 1
}
-Edit, saw your comment. You might be able to adapt the answer to this question

Related

How to Pass a System.Data.DataSet Object as an Argument to a Powershell Workflow?

I'm getting some data from a SQL table, which I then store in a System.Data.DataSet object. I want to pass this data in this DataSet, as an Argument/Parameter, to a workflow, such that I can display all the data in this DataSet in a foreach -parallel style. But I'm at a loss for the correct syntax of passing data from a System.Data.DataSet object to a workflow. Currently I get an Error near the line "param([System.Data.DataSet]$pServiceDataSet)" as shown below.
Function GetSQLData
{
param ($TargetDBServer, $TargetDB, $SQLQuery)
# SQL Connection Object
$sqlConn = New-Object System.Data.SqlClient.SqlConnection
$sqlConn.ConnectionString = "Server=$TargetDBServer;Database=$TargetDB;User Id=SomeUser;Password=SomePassword;"
$sqlConn.Open()
# SQL Command
$sqlcmd = New-Object System.Data.SqlClient.SqlCommand
$sqlcmd.Connection = $sqlConn
$sqlcmd.CommandText = $SQLQuery
# SQL Adapter
$sqlAdp = New-Object System.Data.SqlClient.SqlDataAdapter ($sqlcmd)
# SQL DataSet
$ResultDataSet = New-Object System.Data.DataSet
$sqlAdp.Fill($ResultDataSet) | Out-Null
$sqlConn.Close()
return $ResultDataSet.Tables[0]
}
$CurrentComputerName = $env:COMPUTERNAME
# Export the Windows Services & it's config parameters from the "DatabaseABC..WindowsServicesConfig" Table.
$SQLQueryForService = "
SELECT [ServiceName], [StartUpParameter], [DBServerName], [DBName]
FROM [dbo].[WindowsServicesConfig] WITH (NOLOCK)
WHERE [HostServerName] = '$CurrentComputerName'
AND [ServiceName] LIKE '%MyService%' "
$ServicesDataSet = GetSQLData -TargetDBServer "ServerABC" -TargetDB "DatabaseABC" -SQLQuery $SQLQueryForService
$ServicesDataSet.GetType()
$ServicesDataSet | Format-Table
workflow DisplayAllServices
{
param([System.Data.DataSet]$pServiceDataSet) # <- I get an Error here
foreach -parallel ($Service in $pServiceDataSet)
{
$Service.ServiceName
$Service.StartUpParameter
$Service.DBServerName
$Service.DBName
}
}
DisplayAllServices -pServiceDataSet $ServicesDataSet
My final objective is to use the data in this DataSet to create Windows Services. But this is my most frustrating hurdle. I cannot get past the Error.
Figured out the Solution to my Problem. Replaced the "param([System.Data.DataSet]$pServiceDataSet)" with "param([PSObject]$pServiceDataSet)".

Returning data with sqladapter returns Cannot find an overload for "Fill" and the argument count

I am trying to all the data in table from sql database using powershell. Using that data I can use for a foreach statement and use each field.
I run into the below error:
"MethodException: Multiple ambiguous overloads found for "Fill" and
the argument count: "1"."
I have no clue on how to fix this and help will be appeciated
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = $constring
$SqlConnection.AccessToken = $AccessToken
$SqlConnection.Open()
$SQLQuery = "SELECT DISTINCT servername, DBName FROM DBInfo "
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SQLQuery
$SqlCmd.Connection = $SqlConnection
$ReaderValidate
#SQL Adapter - get the results using the SQL Command
$sqlAdapter = new-object System.Data.SqlClient.SqlDataAdapter
$sqlAdapter.SelectCommand = $SQLQuery
$dataSet = new-object System.Data.Dataset
$allResults = $sqlAdapter.Fill($SqlCmd.CommandText)
Write-output = "$allResults"
#Close SQL Connection
$sqlConnection.Close();
I am using azure function and managed idenitity to access the database. That is the reason I am using "$SqlConnection.AccessToken"
Try to use $DataSet as the argument to the $Adapter.Fill() method:
$allResults = $sqlAdapter.Fill($dataSet)
A good way to think of it, you are filling the data set...
Here is the code I frequently use, though it's adapted using the ::New() method instead of using New-Object, an older version of this was written similar to yours.
$ConnStr = "Server=<ServerName>,<Port>; Database=<DBName>; Integrated Security=true"
$Query = "SELECT * FROM <TableName> WHERE <SomeCondition>"
$Conn = [System.Data.SqlClient.SqlConnection]::new( $ConnStr )
$Conn.Open()
$Adapter = [System.Data.SqlClient.SqlDataAdapter]::new( $Query, $Conn )
$DataSet = [System.Data.DataSet]::new()
[Void]$Adapter.Fill( $DataSet )
$Conn.Close()
These are the adapter overloads:
OverloadDefinitions
-------------------
System.Data.SqlClient.SqlDataAdapter new()
System.Data.SqlClient.SqlDataAdapter new(System.Data.SqlClient.SqlCommand selectCommand)
System.Data.SqlClient.SqlDataAdapter new(string selectCommandText, string selectConnectionString)
System.Data.SqlClient.SqlDataAdapter new(string selectCommandText, System.Data.SqlClient.SqlConnection selectConnection)
In my code I'm using the third one. The command text followed by the already instantiated connection object.

Powershell SQL Server query is creating duplicate entries

I have a ps1 script created to run a query on a SQL Server database. After that I run the rows in a for loop however, the dataset is duplicating entries. If I run the query directly on MS SQL without the script, it does not duplicate rows.
$SQLServer = "bla";
$SQLDBName = "bla";
$uid ="bla";
$pwd = "HIdden_Password";
$App="com.android.app";
$SqlQuery = "SELECT value
FROM obj_user u
JOIN obj_user_device d ON d.id_user = u.id_user
JOIN obj_device od ON od.id_device = d.id_device
JOIN def_device_hardware dh ON dh.id_device_hardware = od.id_device_hardware
JOIN def_device_os do ON do.id_device_os = od.id_device_os
JOIN obj_user_device_app_state das ON das.id_user_device = d.id_user_device
LEFT JOIN obj_device_setting ods ON ods.id_device = od.id_device
LEFT JOIN obj_user_setting ous ON ous.id_user = d.id_user
AND das.bundle_id = '$App'";
Write("Number of results from query:")
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $SQLDBName; Integrated Security = True; User ID = $uid; Password = $pwd;"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$rowCount = $SqlAdapter.Fill($DataSet)
$DataSet.Tables[0]
foreach ($row in $Dataset.tables[0].rows)
{
Write ("entry $row.value")
}
You can see that the $rowcCount shows value of 2 but when I print it it shows 4 entries when it should be 2.
Number of results from query: 2
value
-----
**3a5b220714243811**
105986685869347611614
3a5b220714243811
**105986685869347611614**
The forloop also prints 4 entries
Write ("entry $row.value")
entry 3a5b220714243811
**entry 105986685869347611614**
**entry 3a5b220714243811**
entry 105986685869347611614
Same with out-file from $dataset. NOte: the headings for the SQL columns are not duplicated just the results
$DataSet.Tables[0] | out-file "File.txt";
Found the issue. these two are causing the dups removing one works fine
$SqlAdapter.Fill($DataSet)
$rowCount = $SqlAdapter.Fill($DataSet)

Powershell SQL Query to variables

Trying to use SQL results as variables. This will return the results to the host, but I would like to use each value as a variable.
$job = "22940"
$SQLServer = "my-sql" #use Server\Instance for named SQL instances!
$SQLDBName = "MyDatabase"
$SqlQuery = "DECLARE #Job VARCHAR(5);
SET #Job = '$job'
SELECT dbo.Job.Job, dbo.Job.Customer,
dbo.Job.Note_Text,
dbo.User_Values.Date1 AS 'Setup Date',
dbo.User_Values.Date2 AS 'Due Date'
FROM dbo.Job INNER JOIN
dbo.User_Values ON dbo.Job.User_Values = dbo.User_Values.User_Values
WHERE (dbo.Job.Job = #Job) AND (dbo.Job.User_Values = dbo.User_Values.User_Values)"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $SQLDBName; Integrated Security = True"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
clear
$DataSet.Tables[0]
How can I put each column into a variable that I can use for something else?
I work with data tables directly. You should also look into handling parameters correctly. It helps with handling special characters in the variable value. And, it protects from SQL Injection Attacks:
$Sql = 'select top 100 first_name, Last_name from person where last_name=#lastname'
$Database = 'XXX'
$Server = 'XXX'
$LastName = 'Jones'
$Connection = New-Object System.Data.SqlClient.SqlConnection
$Connection.ConnectionString = "Server=$Server;Database=$Database;Integrated Security=True"
$Connection.Open()
$cmd = new-object system.Data.SqlClient.SqlCommand($Sql, $Connection)
$null = $cmd.Parameters.AddWithValue('#lastname', $LastName)
$DataTable = New-Object System.Data.DataTable
$SqlDataReader = $cmd.ExecuteReader()
$DataTable.Load($SqlDataReader)
$Connection.Close()
$Connection = $null
$DataTable.Rows.Count
foreach ($r in $DataTable.Rows)
{
$fn = $r.first_name
$ln = $r.last_name
"$fn $ln"
}
Not sure if this is really the correct way but it does give me the results I am looking for. Looked through some of my notes on working with CSV files and I added the foreach at the end. The above comments from #notjustme and #jcoehoorn got me looking in the right direction. Thanks
$job = "22940"
$SQLServer = "my-sql" #use Server\Instance for named SQL instances!
$SQLDBName = "MyDatabase"
$SqlQuery = "DECLARE #Job VARCHAR(5);
SET #Job = '$job'
SELECT dbo.Job.Job, dbo.Job.Customer,
dbo.Job.Note_Text,
dbo.User_Values.Date1 AS 'Setup Date',
dbo.User_Values.Date2 AS 'Due Date'
FROM dbo.Job INNER JOIN
dbo.User_Values ON dbo.Job.User_Values = dbo.User_Values.User_Values
WHERE (dbo.Job.Job = #Job) AND (dbo.Job.User_Values = dbo.User_Values.User_Values)"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $SQLDBName; Integrated Security = True"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
clear
foreach($Job in $DataSet.Tables[0])
{
$MyJobNumber = $Job.Job
$MyCustomer = $Job.Customer
$MyNotes = $Job.Note_Text
$MySetupDate = $Job.Setup_Date
$MyDueDate = $Job.Due_Date
}

sp_msforeachdb in dataset

Can someone tell me why the following code only returns one row from the DataSet (for the master db) instead of one for each database on the server?
$SQLConn = New-Object System.data.SqlClient.SqlConnection
$SQLConn.ConnectionString = "Data Source = $SQLServer; Initial Catalog = master;
Integrated Security = True"
$SQLConn.Open()
$query = "exec sp_msForEachDb 'use [?] SELECT TOP 1 [name] FROM sysusers'"
$SQLCmd = New-Object System.Data.Sqlclient.SqlCommand($query, $SQLConn);
$SQLAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SQLAdapter.SelectCommand = $SQLCmd
$DataSet = New-Object System.Data.Dataset
$SQLAdapter.Fill($DataSet) | out-null
ForEach ($row in $DataSet.Tables[0])
{
$Name = $row["name"]
write-host $Name
}
$SQLConn.Close()
Your code shows your using the index position 0 of $dataset.tables. Change it to $DataSet.Tables
ForEach ($row in $DataSet.Tables[0])
{
$Name = $row["name"]
write-host $Name
}
Change to...
ForEach ($row in $DataSet.Tables)
{
$Name = $row["name"]
write-host $Name
}
You're returning getting multiple datatables in your result set, but only showing the first one Tables[0]. Try this:
$DataSet.Tables | foreach {$_.name}
Here's some completed and tested code to combine result set:
$SQLServer = ".\SQL1"
$query = #"
create table #output
(DB varchar(128),
name varchar(128)
)
exec sp_MSforeachdb #command1='USE [?];
insert #output SELECT TOP 1 ''?'' AS ''DB'', [name]
FROM sysusers';
select * from #output
"#
$SQLConn = New-Object System.data.SqlClient.SqlConnection
$SQLConn.ConnectionString = "Data Source = $SQLServer; Initial Catalog = master;
Integrated Security = True"
$SQLConn.Open()
$SQLCmd = New-Object System.Data.Sqlclient.SqlCommand($query, $SQLConn);
$SQLAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SQLAdapter.SelectCommand = $SQLCmd
$DataSet = New-Object System.Data.Dataset
$SQLAdapter.Fill($DataSet) | out-null
$DataSet.Tables[0]
$SQLConn.Close()
I think I needed to do something like this to get all of the records into one dataset:
$query = "DECLARE #users TABLE ([name] varchar(100)) INSERT #db_roles
exec sp_msForEachDB 'USE [?] SELECT [name] FROM sysusers'
SELECT [name] FROM #db_roles"