Exporting powershell script output to local SQLtable - sql-server

I am trying to export the output of the below script to a local sql table directly instead of using CSV as a mediator. Is there a way to export the output of the below script directly to the local sql table.
Get-Content "C:\test\computers.txt" | Where-Object { $_.Trim() -ne "" } |
ForEach-Object {
Invoke-Command -Computer $_ -ScriptBlock {
Param($computer)
$Database = "secaudit"
$AttachmentPath = "C:\test\SQLData.csv"
$SqlQuery = "xp_fixeddrives"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Data Source=$computer;Initial Catalog=$Database;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
$nRecs = $SqlAdapter.Fill($DataSet)
$nRecs | Out-Null
$objTable = $DataSet.Tables[0]
$DataSet.Tables[0]
} -ArgumentList $_ -Credential $cred
} | Select-Object PSComputerName, Drive, "MB Free" |
Export-Csv -Path "C:\test\output_space.csv" -NoTypeInformation
$query = #"
BULK INSERT [Test1].[dbo].[table_1] FROM "C:\test\output_space.csv" WITH (FIRSTROW = 2, FIELDTERMINATOR = ",", ROWTERMINATOR = "\n")
"#
sqlcmd -S "CSCINDAE680687" -E -Q $query

Here's an example function that takes a connection string, target table name, and a data table of the rows to be loaded. By default, columns are mapped by ordinal but you can use SQlBulkCopyColumnMappings to specify different mappings, if needed.
Function Insert-TargetTable
{
param(
[Parameter(Mandatory=$True)]
[string]$TargetDatabaseConnectionString
, [Parameter(Mandatory=$True)]
[string]$TargetTableName
, [Parameter(Mandatory=$True)]
[System.Data.DataTable]$DataTable
)
$bcp = New-Object System.Data.SqlClient.SqlBulkCopy($TargetDatabaseConnectionString);
$bcp.DestinationTableName = $TargetTableName;
$bcp.WriteToServer($DataTable);
$bcp.Close();
}

Related

PowerShell: Create Hashtable with three columns

Ultimately, I'm trying to create a script that will get all Windows Services Running as a domain Service Account from a list of remote machines and output a csv file with three columns: the Service Account Name, the Windows Service, and the Hostname. I cannot figure out how to create the hashtable with two arrays. I've had some success with just one key and one array using += but even that has some issues and I'm reading this is inefficient.
This is modified code that gets all Win Services running as System on my local system:
$server = $env:COMPUTERNAME
$tgtAcct = 'SYSTEM'
$reportCsv = Join-Path -Path ([Environment]::GetFolderPath("Desktop")) -ChildPath ("report.$(Get-Date -Format `"yyyMMdd_hhmmss`").csv")
$GetServiceAccounts = {
[CmdletBinding()]
param(
$hostname
)
$serviceList = #( Get-WmiObject -Class Win32_Service -ComputerName $hostname -Property Name, StartName, SystemName -ErrorAction Stop )
$serviceList
}
Function Process-CompletedJobs(){
$jobs = Get-Job -State Completed
ForEach ($job in $jobs) {
$data = Receive-Job $job
Remove-Job $job
If ($data.GetType() -eq [System.Object[]]) {
$serviceList = $data | Where-Object { $_.StartName -ne $null -and $_.StartName.ToUpper().Contains($tgtAcct) }
ForEach ($service in $serviceList) {
$account = $service.StartName
$winService = $service.Name
$occurance = $service.SystemName
}
}
}
}
Start-Job -ScriptBlock $GetServiceAccounts -Name "read_$($server)" -ArgumentList $server | Wait-Job > $null
Process-CompletedJobs
Here is what I've tried that isn't working:
$server = $env:COMPUTERNAME
$tgtAcct = 'SYSTEM'
$serviceAccounts = #{}
$accountTable = #()
$winSvcTable = #()
$occurTable = #()
$reportCsv = Join-Path -Path ([Environment]::GetFolderPath("Desktop")) -ChildPath ("report.$(Get-Date -Format `"yyyMMdd_hhmmss`").csv")
$GetServiceAccounts = {
[CmdletBinding()]
param(
$hostname
)
$serviceList = #( Get-WmiObject -Class Win32_Service -ComputerName $hostname -Property Name, StartName, SystemName -ErrorAction Stop )
$serviceList
}
Function Process-CompletedJobs(){
$jobs = Get-Job -State Completed
ForEach ($job in $jobs) {
$data = Receive-Job $job
Remove-Job $job
If ($data.GetType() -eq [System.Object[]]) {
$serviceList = $data | Where-Object { $_.StartName -ne $null -and $_.StartName.ToUpper().Contains($tgtAcct) }
ForEach ($service in $serviceList) {
$account = $service.StartName
$winService = $service.Name
$occurance = $service.SystemName
$script:serviceAccounts.Item($account) += $winService
$script:serviceAccounts.Item($account) += $occurance
}
}
}
}
Start-Job -ScriptBlock $GetServiceAccounts -Name "read_$($server)" -ArgumentList $server | Wait-Job > $null
Process-CompletedJobs
ForEach ($serviceAccount in $serviceAccounts.Keys) {
ForEach ($occurance in $serviceAccounts.Item($serviceAccount)) {
ForEach ($winService in $serviceAccounts.Item($serviceAccount)) {
$row = New-Object PSObject
Add-Member -InputObject $row -MemberType NoteProperty -Name "Account" -Value $serviceAccount
Add-Member -InputObject $row -MemberType NoteProperty -Name "Service" -Value $winService
Add-Member -InputObject $row -MemberType NoteProperty -Name "Hostname" -Value $occurance
$accountTable += $row
}
}
}
$accountTable | Export-Csv $reportCsv
I'm trying to modify code written by Andrea Fortuna that almost does what I want but want to split the second column into two. Again, I'm also looking for how to do this without adding to each array using += if possible. https://www.andreafortuna.org/2020/03/25/windows-service-accounts-enumeration-using-powershell/
If your goal is to export to CSV, then a single top-level hashtable is not the data structure you want.
Export-Csv will expect a collection of individual objects, so that's what you'll want to create:
Function Process-CompletedJobs(){
$jobs = Get-Job -State Completed
ForEach ($job in $jobs) {
$data = Receive-Job $job
Remove-Job $job
If ($data.GetType() -eq [System.Object[]]) {
$serviceList = $data | Where-Object { $_.StartName -ne $null -and $_.StartName.ToUpper().Contains($tgtAcct) }
ForEach ($service in $serviceList) {
# don't assign this new object to anything - let it "bubble up" as output instead
[pscustomobject]#{
Account = $service.StartName
Service = $service.Name
Occurrence = $service.SystemName
}
}
}
}
}
Now you can do:
Start-Job -ScriptBlock $GetServiceAccounts -Name "read_$($server)" -ArgumentList $server | Wait-Job > $null
Process-CompletedJobs |Export-Csv ...
What about this?
$server = $env:COMPUTERNAME
$tgtAcct = 'SYSTEM'
$reportCsv = Join-Path -Path ([Environment]::GetFolderPath("Desktop")) -ChildPath ("report.$(Get-Date -Format `"yyyMMdd_hhmmss`").csv")
$GetServiceAccounts = {
[CmdletBinding()]
param(
$hostname
)
$serviceList = #( Get-WmiObject -Class Win32_Service -ComputerName $hostname -Property Name, StartName, SystemName -ErrorAction Stop )
$serviceList
}
Function Process-CompletedJobs(){
$jobs = Get-Job -State Completed
$hashtable = #{}
ForEach ($job in $jobs) {
$data = Receive-Job $job
Remove-Job $job
If ($data.GetType() -eq [System.Object[]]) {
$serviceList = $data | Where-Object { $_.StartName -ne $null -and $_.StartName.ToUpper().Contains($tgtAcct) }
ForEach ($service in $serviceList) {
$account = $service.StartName
$winService = $service.Name
$occurance = $service.SystemName
$hashtable[$account] = #{winService = $winService; occurance = $occurance}
}
}
}
return $hashtable
}
Start-Job -ScriptBlock $GetServiceAccounts -Name "read_$($server)" -ArgumentList $server | Wait-Job > $null
$myHashTable = Process-CompletedJobs
Make it simple it will work, this is the complete script and I have tried and valid for many servers you can change the variable $hostnames = $env:COMPUTERNAME,host2,host3,.. as you need
and I added some parameters to get a grid view of result to test and add force and notypeinfo. in export-csv
Here is the code - I hope you mark it answer if it helps:
$hostnames = $env:COMPUTERNAME
$tgtAcct = 'SYSTEM'
$reportCsv = Join-Path -Path ([Environment]::GetFolderPath("Desktop")) -ChildPath ("report.$(Get-Date -Format `"yyyMMdd_hhmmss`").csv")
$TableName = "System Accounts"
#Create a table
$Table = new-object System.Data.DataTable "$TableName"
#Create a column and you can increase it as many as you need
$col1 = New-Object System.Data.DataColumn "Service Account Name",([string])
$col2 = New-Object System.Data.DataColumn "Windows Service",([string])
$col3 = New-Object System.Data.DataColumn "Hostname",([string])
# Add the Columns
$Table.columns.add($col1)
$Table.columns.add($col2)
$Table.columns.add($col3)
foreach($hostname in $hostnames){
$serviceList = Get-WmiObject -Class Win32_Service -ComputerName $hostname -Property Name, StartName, SystemName -ErrorAction Stop | Where-Object { $_.StartName -ne $null -and $_.StartName.ToUpper().Contains($tgtAcct) }
foreach ($service in $serviceList){
$Row = $Table.NewRow()
$Row."Hostname" = $hostname
$Row."Service Account Name" = $service.StartName
$servicename = $service.Name.ToString()
$Row."Windows Service" = $servicename
$Table.Rows.Add($Row)
}}
$Table | Out-GridView
$Table | Export-Csv $reportCsv -Force -NoTypeInformation

How do I retrieve returned value from sql function in Powershell?

Hi I am using Powershell script to call a function which returns me table name and I am unable to understand how to retrieve the value. Below is the code that I am using
$SqlQuery = "select dbo.abc_import_create_table_for_file('$fileFullName', '$(gc $fileFullName | select -first 1)');"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $SQLDBName; 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)
$SqlConnection.Close()

Strange linebreaks occur when exporting to csv from Datatable

I have a strange issue when I export my DataTable to CSV with Powershell: I get some line breaks and I am also not sure why I get the
#TYPE System.Data.DataRow
$query = "use [ISTABLocalDB]
SELECT
Item.[ID] as PartIdDB
,car.Kilometrage
FROM [ISTABLocalDB].[file].[Item] as Item
INNER JOIN [ISTABLocalDB].[file].[ItemPart] as ItemPart ON Item.ID = ItemPart.
LEFT JOIN [ISTABLocalDB].[file].[ItemResourceFile] as ImageFile ON Car.ID = ImageFile.Item_ID
where
Item.Type = 'P' -- means parts
and ItemType.[Code] Not like '9001' -- car saved
and ItemType.[Code] in ('5000','5001','5002','5003','5003','5005','5006','9000');"
$extractFile = "$path $date.csv"
$connectionTemplate = "Data Source={0};Integrated Security=SSPI;Initial Catalog={1};"
$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.Tables[0] | Export-Csv $extractFile -encoding "unicode" -Delimiter ";"
$file= $extractFile
(Get-Content $file) | Foreach-Object {$_ -replace '"', ''}|Out-File $file

How do I run a script on each output of Get-ChildObject

Creating a script to change the ACL on entire directories recursively. The simple script changes the ACL accordingly on one file, however I do not know how to run the script on each file of Get-ChildItem
Get-ChildItem $directory –recurse | % { Write-host $_.FullName }
This outputs the appropriate list of directory/file names
$acl = Get-Acl $file
$permission = "domain/user","FullControl","Allow"
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $permission
$acl.SetAccessRule($accessRule)
$acl | Set-Acl $file
Is there a way to set each output of Get-ChildItem as $file? I was trying to read up on ForEach-Object but I haven't been able to get the syntax right.
You can embed the code you already have in a foreach loop. Just get an array of the files by assigning the output of the Get-ChildItem call to a variable first:
$files = Get-ChildItem $directory -recurse
foreach($file in $files) {
$acl = Get-Acl $file
$permission = "domain/user","FullControl","Allow"
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $permission
$acl.SetAccessRule($accessRule)
$acl | Set-Acl $file
}
You can try this one
Get-Childitem $directory | ForEach {
$file = $_
$acl = Get-Acl $file
$permission = "domain/user","FullControl","Allow"
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $permission
$acl.SetAccessRule($accessRule)
$acl | Set-Acl $file
}
I would simply use the current object variable ($_):
Get-ChildItem $directory –Recurse | % {
$acl = Get-Acl -LiteralPath $_
$permission = 'domain\user', 'FullControl', 'Allow'
$accessRule = New-Object Security.AccessControl.FileSystemAccessRule $permission
$acl.SetAccessRule($accessRule)
Set-Acl -AclObject $acl -LiteralPath $_
}
If you want to put the ACL modification into a script and separate it from the Get-ChildItem I'd suggest to make the script process pipelined input:
[CmdletBinding()]
Param(
[Parameter(
Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true
)]
[IO.FileSystemInfo]$Path
)
Begin {
$permission = 'domain\user', 'FullControl', 'Allow'
$accessRule = New-Object Security.AccessControl.FileSystemAccessRule $permission
}
Process {
$acl = Get-Acl -LiteralPath $Path
$acl.SetAccessRule($accessRule)
Set-Acl -AclObject $acl -LiteralPath $Path
}
Note, however, that Get-Acl cannot modify ACLs where neither your account nor one of your groups is the owner. You can work around this issue by using icacls:
[CmdletBinding()]
Param(
[Parameter(
Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true
)]
[IO.FileSystemInfo]$Path
)
Begin {
$trustee = 'domain\user'
$permission = 'F'
}
Process {
& icacls $Path.FullName "/grant:r" "${trustee}:(CI)(OI)${permission}" | Out-Null
}

Using += in scriptblock for a switch statement in a foreach loop

Firstly, thank you for taking the time to read this, thank you for your help in advance.
Here is my code:
<#
SCCM Request Alert Script
#>
Import-Module ActiveDirectory
$WMIObjects = Get-WmiObject -Namespace 'ROOT\SMS\Site_EUR' -Class SMS_UserApplicationRequest -ComputerName "EUR-SCCM"
$FileStore = "c:\export\SCCMRequestfile.txt"
foreach ($Obj in $WMIObjects)
{
[String]$RequestValue = $Obj.CurrentState
$Application = $Obj.Application
$User = $Obj.User -replace 'MYDOMAIN\\',""
$ADUser = Get-ADUser -Identity $User
$PendingRequest = #()
$CancelledRequest = #()
$DeniedRequest = #()
$ApprovedRequest = #()
$Unknown = #()
$Args = #{ 'User' = $ADUser.Name; 'Application' = $Obj.Application }
$PR = New-Object -TypeName PSObject -Property $Args
$CR = New-Object -TypeName PSObject -Property $Args
$DR = New-Object -TypeName PSObject -Property $Args
$AR = New-Object -TypeName PSObject -Property $Args
$UR = New-Object -TypeName PSObject -Property $Args
switch ($RequestValue) {
1 { $PendingRequest += $PR }
2 { $CancelledRequest += $CR }
3 { $DeniedRequest += $DR }
4 { $ApprovedRequest += $AR }
default { $Unknown += $UK }
}
}
Write-Host -ForegroundColor 'yellow' "Pending Requests "
$PendingRequest
Write-Host -ForegroundColor 'DarkYellow' "Cancelled Requests "
$CancelledRequest
Write-Host -ForegroundColor 'DarkRed' "Denied Requests "
$DeniedRequest
Write-Host -ForegroundColor 'Green' "Approved Requests "
$ApprovedRequest
Write-Host -ForegroundColor 'White' "Unknown Approval Type "
$Unknown
At them moment it only returns the last object in the foreach loop.
I've tested a foreach loop manually using;
foreach ($Obj in $Objects) {
$array = #()
$array += $Obj
}
$Array
And this places each object in the the array.
So I was wondering if this was an issue with the switch statement or something I haven't done like casting it as an array?
Any help would be appreciate, Thank you.
Nigel Tatschner
You're re-initializing your arrays on each iteration of the loop. Move the following lines so they're before your foreach loop:
$PendingRequest = #();
$CancelledRequest = #();
$DeniedRequest = #();
$ApprovedRequest = #();
$Unknown = #();

Resources