ForEach Directory Lookup and InputObject Powershell - powershell

Can someone help me and point out why my script isn't looping through my server list and not inserting all the data to my sql table?
$SQLserverDB = #()
$SQLserverDB = get-content "D:\Temp\servers.txt"
foreach ($SQLserver in $SQLserverDB) {
$server = New-Object Microsoft.sqlserver.management.smo.server($SQLserver)
Write-Host "--Server List:"$server"---" -ForegroundColor Yellow
foreach ($db in $Server.Databases) {
$dbobj = New-Object PSObject -Property ([ordered]#{
Instance_Name = $db.Parent
Database_Name = $db.Name
Database_ID = $db.id
Active = $db.Status
DateStamp = $db.CreateDate
Owner = $db.Owner
Compatibility_Level = $db.compatibilitylevel
Recovery_Model = $db.recoverymodel
DB_Size_MB = [Math]::round($db.size, 2)
}
)
}
}
$dt = Out-DbaDataTable -InputObject $dbobj
Write-SqlTableData -ServerInstance localhost -DatabaseName DBA_Central -SchemaName dbo -TableName database_masters2 -InputData $dt

Related

PowerShell Import-CSV format datetime: Data is be represented as 00:00:0 in the file output when the file has 2021-01-01 00:00:00.000

I have tried formatting the dates and have not had any luck.
Sample data:
2021-01-01 00:00:00.000,2021-01-24 00:00:00.000,2021-01-01 00:00:00.000,15,17,15,17,22,402219.89,402219.89,457621.22,457621.22,597385.79,10,1855180.96,169651.66,1235781.38,6.30,155963.88,155963.88,188556.81,188556.81,-214.63,-155.97
$data = import-csv $impcsv -header 'StartDate', 'EndDate', 'MonthStart','ytdDays', 'pyDays',
'MTDDays','PMTDDays','PFullMTDDays','ytd', 'mtd', 'pytd', 'pmtd', 'pfullmtd', 'whs', 'inventory',
'onorderinventory', 'onpo', 'yearpct', 'ytd_gp', 'mtd_gp', 'pytd_gp', 'pmtdd_gp', 'daily',
'daily_gp'
$count = 1
foreach($i in $data){
[string]$StartDate = $I.StartDate
[string]$EndDate = $I.EndDate
[string]$MonthStart = $i.MonthStart
[single]$ytdDays = $i.ytdDays
[single]$pyDays = $i.pyDays
[single]$MTDDays = $i.MTDDays
[single]$PMTDDays = $i.PMTDDays
[single]$PFullMTDDays = $i.PFullMTDDays
[single]$ytd = $i.ytd
[single]$MTD = $i.MTD
[single]$pytd = $i.pytd
[single]$pmtd = $i.pytd
[single]$pfullmtd = $i.pfullmtd
[string]$whs = $i.whs
[single]$inventory = $i.inventory
[single]$onorderinventory = $i.onorderinventory
[single]$onpo = $i.onpo
[single]$yearpct = $i.yearpct
[single]$ytd_gp = $i.ytd_gp
[single]$mtd_gp = $i.mtd_gp
[single]$pytd_gp = $i.pytd_gp
[single]$pmtdd_gp = $i.pmtdd_gp
[single]$daily= $i.daily
[single]$daily_gp = $i.daily_gp
$query = "INSERT INTO $table (StartDate, EndDate, MonthStart, ytdDays, pyDays,
MTDDays,PMTDDays,PFullMTDDays, ytd, mtd, pytd, pmtd, pfullmtd, whs,
inventory,onorderinventory, onpo, yearpct, ytd_gp, mtd_gp, pytd_gp,
pmtdd_gp, daily, daily_gp)
VALUES ('$StartDate', '$EndDate', '$MonthStart', '$ytdDays', '$pyDays',
'$MTDDays','$PMTDDays','$PFullMTDDays','$ytd', '$mtd', '$pytd', '$pmtd',
'$pfullmtd', '$whs', '$inventory', '$onorderinventory', '$onpo',
'$yearpct','$ytd_gp','$mtd_gp', '$pytd_gp','$pmtdd_gp', '$daily', '$daily_gp')"
invoke-sqlcmd -Database $databaseName -ServerInstance $ServerName -Username $username -Password
$password -Query $query
write-host $startDate
write-host "Processing row ..........$count" -foregroundcolor green
$count = $count + 1
}

Data type mismatch when querying a CSV with ACE OLEDB provider

I am attempting to query a CSV file using the Microsoft ACE OLEDB provider. When I add "PrctBusy > 60" to the where clause I receive the Error "Data type mismatch in criteria expression." I have searched StackOverFlow and used google to search for solutions, I see this is not an uncommon issue. From my readings it looks to be datatype issue. The data in the column PrctBusy is all numeric. I think I need to force it to be number but I have not found a solution.
Below is the code I am currently working with:
$ArrayNameUtil = "000198701258"
$CatNameUtil = "FE_DIR"
$sdLocalPath = "D:\Logs\SANData\Perf"
$InputCSV = "VMaxSANReportUtilFile.csv"
$csv = Join-Path $sdLocalPath $InputCSV
$provider = (New-Object System.Data.OleDb.OleDbEnumerator).GetElements() | Where-Object { $_.SOURCES_NAME -like "Microsoft.ACE.OLEDB.*" }
if ($provider -is [system.array]) { $provider = $provider[0].SOURCES_NAME } else { $provider = $provider.SOURCES_NAME }
$connstring = "Provider=$provider;Data Source=$(Split-Path $csv);Extended Properties='text;HDR=$firstRowColumnNames;';"
$firstRowColumnNames = "Yes"
$delimiter = ","
$tablename = (Split-Path $csv -leaf).Replace(".","#")
$conn = New-Object System.Data.OleDb.OleDbconnection
$conn.ConnectionString = $connstring
$provider = (New-Object System.Data.OleDb.OleDbEnumerator).GetElements() | Where-Object { $_.SOURCES_NAME -like "Microsoft.ACE.OLEDB.*" }
if ($provider -is [system.array]) { $provider = $provider[0].SOURCES_NAME } else { $provider = $provider.SOURCES_NAME }
$connstring = "Provider=$provider;Data Source=$(Split-Path $csv);Extended Properties='text;HDR=$firstRowColumnNames;';"
$firstRowColumnNames = "Yes"
$delimiter = ","
$tablename = (Split-Path $csv -leaf).Replace(".","#")
$conn = New-Object System.Data.OleDb.OleDbconnection
$conn.ConnectionString = $connstring
$conn.Open()
#
$sql = "SELECT TimeStamp, count(PrctBusy) AS Above60 FROM [$tablename] WHERE array = '$ArrayNameUtil' and Category like '$CatNameUtil' and PrctBusy > 60 Group by TimeStamp "
$cmd = New-Object System.Data.OleDB.OleDBCommand
$cmd.Connection = $conn
$cmd.CommandText = $sql
$dtp = New-Object System.Data.DataTable
$dtp.Load($cmd.ExecuteReader())
Because of the pointer from TessellatingHeckler to Codeproject and some follow on queries, I was lead to http://aspdotnetcodes.com/Importing_CSV_Database_Schema.ini.aspx. I found that a schema.ini file in the same directory as the CSV file could specify the data type.
The schema.ini file ended up in the following format:
[VMaxSANReportUtilFile.csv]
ColNameHeader=True
Format=CSVDelimited
Col1=Array Text Width 20
Col2=TimeStamp Text Width 20
Col3=Category Text Width 20
Col4=Instance Text Width 20
Col5=PrctBusy Short
Col6=QueUtil Short
I went through several revisions to get the data type correct for an ACE OLE DB provider. If the columns are named the names need to be in the schema.ini file.

How do I get around this error in PowerShell? Exception setting "Rtf": "Error creating window handle."

This has no issue with SQL. it's when creating the rtf object.
I am connecting to a sql database and pulling information. Some of the information is html,rtf, and plain text. After about 10 mins of running I get this:
Exception setting "Rtf": "Error creating window handle."
At line:24 char:76
+ ... Name System.Windows.Forms.RichTextBox; $rtf.Rtf = $convo.Body; $body ...
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting
Has anyone else ran into this issue?
Here is the script itself.
#Who are you searching for?
#Example User ID: user#domain.com
$Subject = "changeme#domain.com"
#Set the date to search from
#Example date format: 2016-08-16.
#Leave it blank if you don't want to search for just dates.
$Date = ""
#Blank array to store the conversation history
$arr = #()
#Lync Archive Server
$SQLSvr = "ServerName Goes Here"
#Lync Archive Database
$Database = "LcsLog"
#Get the UserId's
$UserUri = Invoke-Sqlcmd -Query "Select UserUri,UserId From dbo.Users u;" -ServerInstance $SQLSvr -Database $Database
#Build the Select Statement
$select = "Select * from dbo.Users d left join dbo.Messages m on FromId = d.UserId or ToId = d.UserId Where d.UserUri = '$Subject' "
if($Date)
{
$select = $select +"and m.MessageIdTime >= '$Date 00:00:01.550' order by m.MessageIdTime asc;"
}
else
{
$select = $select + "order by m.MessageIdTime asc;"
}
#Get the conversation history
$ConvoData = Invoke-Sqlcmd -Query ($select) -ServerInstance $SQLSvr -Database $Database;
#Loop through each conversation
foreach($convo in $ConvoData)
{
#Loop through each user.
foreach($user in $UserUri)
{
#Verify the FromId
if($convo.FromId -eq $user.UserId)
{
$FromID = $user.UserUri
}
#Verify the ToId
if($convo.ToId -eq $user.UserId)
{
$ToId = $user.UserUri
}
}
#Parse the body for legible reading
switch ($convo.ContentTypeId)
{
'1' {$html = New-Object -ComObject "HTMLFile"; $html.IHTMLDocument2_write($convo.Body);$body = $html.IHTMLDocument2_body.innerText; $html.close();}
'2' {$rtf = New-Object -TypeName System.Windows.Forms.RichTextBox; $rtf.Rtf = $convo.Body; $body = $rtf.Text; $rtf.Clear();}
'3' {$body = $convo.Body}
}
#Build the Message Output
$obj = New-Object -TypeName psobject -Property #{User = $Subject; "Message Time" = $convo.MessageIdTime; From = $FromID; To = $ToId; Body = $body}
#Add data to the array
$arr += $obj
}
$arr | Select User,"Message Time",From,To,Body | Export-csv "$env:userprofile\desktop\$Subject - conversation report.csv"
Not really an answer, but a recommendation that you should turn your parameters into a Param block. It would be more useful if you wanted to call the script from the command line or turn it into a function.
Param (
# Parameter Subject
[Parameter(Mandatory = $true,
HelpMessage = 'Who are you searching for? e.g. User ID: user#domain.com')]
$Subject = 'changeme#domain.com',
# Parameter Date
[Parameter(HelpMessage = 'Set the date to search from. e.g. "2016-08-16"')]
[String]
$Date,
# Parameter SQLSvr
[Parameter(Mandatory = $true,
HelpMessage = 'ServerName Goes Here')]
$SQLSvr,
# Parameter Database
[Parameter(Mandatory = $true,
HelpMessage = 'Lync Archive Database')]
$Database = 'LcsLog'
)
I figured it out. By creating one instance of my RTF object at the beginning it corrected my error.
#Who are you searching for?
#Example User ID: user#domain.com
$Subject = "changeme#domain.com"
#Set the date to search from
#Example date format: 2016-08-16.
#Leave it blank if you don't want to search for just dates.
$Date = ""
#Blank array to store the conversation history
$arr = #()
#Create RTF and HTML Objects
$html = New-Object -ComObject "HTMLFile";
$rtf = New-Object -TypeName System.Windows.Forms.RichTextBox;
#Lync Archive Server
$SQLSvr = "Server Name goes here"
#Lync Archive Database
$Database = "LcsLog"
#Get the UserId's
$UserUri = Invoke-Sqlcmd -Query "Select UserUri,UserId From dbo.Users u;" -ServerInstance $SQLSvr -Database $Database
#Build the Select Statement
$select = "Select * from dbo.Users d left join dbo.Messages m on FromId = d.UserId or ToId = d.UserId Where d.UserUri = '$Subject' "
if($Date)
{
$select = $select +"and m.MessageIdTime >= '$Date 00:00:01.550' order by m.MessageIdTime asc;"
}
else
{
$select = $select + "order by m.MessageIdTime asc;"
}
#Get the conversation history
$ConvoData = Invoke-Sqlcmd -Query ($select) -ServerInstance $SQLSvr -Database $Database;
#Loop through each conversation
foreach($convo in $ConvoData)
{
#Loop through each user.
foreach($user in $UserUri)
{
#Verify the FromId
if($convo.FromId -eq $user.UserId)
{
$FromID = $user.UserUri
}
#Verify the ToId
if($convo.ToId -eq $user.UserId)
{
$ToId = $user.UserUri
}
}
#Parse the body for legible reading
switch ($convo.ContentTypeId)
{
'1' {$html.IHTMLDocument2_write($convo.Body);$body = $html.IHTMLDocument2_body.innerText; $html.close();}
'2' {$rtf.Rtf = $convo.Body; $body = $rtf.Text; $rtf.Clear();}
'3' {$body = $convo.Body}
}
#Build the Message Output
$obj = New-Object -TypeName psobject -Property #{User = $Subject; "Message Time" = $convo.MessageIdTime; From = $FromID; To = $ToId; Body = $body}
#Add data to the array
$arr += $obj
}
$arr | Select User,"Message Time",From,To,Body | Export-csv "$env:userprofile\desktop\$Subject - conversation report.csv"

Opening a SSRS project using Powershell

I have a report that is copied to a number of different servers. It is imported manually and the data source properties are altered to match the current server's specs. I would like to be able to automate the process by enabling users to open a the SSRS report and dynamically alter it's shared data source properties through PowerShell. I hope you could help. You may see reference below.
The script would accept an input parameter for servername, username and password. Also, the save my password must be ticked.
I couldn't believe I managed to create a script for this. You may make use of the script below as future reference. Comments are available for each part and anything that needs to be altered has a "here" keyword , ex. Your_database_name_here .
Import-Module SqlPs
#Input parameter to get Server\Instancename of your Datasource
$Servername = Read-Host "Please enter your Servername"
$Instancename = Read-Host "Please enter your Instancename. For default instance please press enter"
Write-host ""
if ($Instancename -eq ""){
$ServerInstance = $Servername
}
Else {
$ServerInstance = $Servername +"\"+ $InstanceName
}
#Setting up SSRS Target URL. This is the location where your reports would be deployed.
if ($Instancename -eq ""){
$ReportServerUri = "http://$Servername/ReportServer//ReportService2010.asmx?wsdl"
$TargetURL = "http://$Servername/Reports"
}
Else {
$ReportServerUri = "http://$Servername/ReportServer_$Instancename//ReportService2010.asmx?wsdl"
$TargetURL = "http://$Servername/Reports_$Instancename"
}
$global:proxy = New-WebServiceProxy -Uri $ReportServerUri -UseDefaultCreden
#We would make use of SQL Server Authentication for the reports shared datasource so you need to supply a username and password.
Write-Host " SQL Server Authentication:"
$Username = Read-Host " Username"
$Password = Read-Host -AsSecureString "Password"
$type = $Proxy.GetType().Namespace
$datatype = ($type + '.Property')
$property =New-Object ($datatype);
$property.Name = “NewFolder”
$property.Value = “NewFolder”
$numproperties = 1
$properties = New-Object ($datatype + '[]')$numproperties
$properties[0] = $property;
$newFolder = $proxy.CreateFolder("Reports”, “/”, $properties);
$newFolder = $proxy.CreateFolder("Data Sources”, “/”, $properties);
$Children =$proxy.ListChildren("/",$false)
$DBname = 'Your_Database_Name_Here'
# Creating Datasource through powershell
Write-Host " Creating Datasource ..."
$Name = "Name_Your_Datasource_here"
$Parent = "/Data Sources"
$ConnectString = "data source=$Servername\$Instancename;initial catalog=$DBname"
$type = $Proxy.GetType().Namespace
$DSDdatatype = ($type + '.DataSourceDefinition')
$DSD = new-object ($DSDdatatype)
if($DSD -eq $null){
Write-Error Failed to create data source definition object
}
$CredentialDataType = ($type + '.CredentialRetrievalEnum')
$Cred = new-object ($CredentialDataType)
$CredEnum = ($CredentialDataType).Integrated
$Cred.value__=1
$DSD.CredentialRetrieval =$Cred
$DSD.ConnectString = $ConnectString
$DSD.Enabled = $true
$DSD.EnabledSpecified = $false
$DSD.Extension = "SQL"
$DSD.ImpersonateUserSpecified = $false
$DSD.Prompt = $null
$DSD.WindowsCredentials = $false
$DSD.UserName = $Username
$DSD.Password = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password))
$newDSD = $proxy.CreateDataSource($Name,$Parent,$true,$DSD,$null)
#Deploying RLD files to Target URL
Write-Host " Deploying RDL files ..."
$stream = Get-Content 'D:\Your_RDL_path_here.rdl' -Encoding byte
$warnings =#();
$proxy.CreateCatalogItem("Report","Report_Name_here","/Reports",$true,$stream,$null,[ref]$warnings)
#Let's make use of the datasource we just created for your RDL files.
$Items = $global:proxy.listchildren("/Data Sources", $true)
foreach ($item in $items)
{
$DatasourceName = $item.Name
$DatasourcePath = $item.Path
}
$RDLS = $global:proxy.listchildren("/Reports", $true)
foreach ($rdl in $rdls)
{
$report = $rdl.path
$rep = $global:proxy.GetItemDataSources($report)
$rep | ForEach-Object {
$proxyNamespace = $_.GetType().Namespace
$constDatasource = New-Object ("$proxyNamespace.DataSource")
$constDatasource.Name = $DataSourceName
$constDatasource.Item = New-Object ("$proxyNamespace.DataSourceReference")
$constDatasource.Item.Reference = $DataSourcePath
$_.item = $constDatasource.Item
$global:proxy.SetItemDataSources($report, $_)
Write-Host "Changing datasource `"$($_.Name)`" to $($_.Item.Reference)"
}
}
#Open a IE browser to view the report.
$IE=new-object -com internetexplorer.application
$IE.navigate2($TargetURL)
$IE.visible=$true
Write-Host ""
Write-Host "You may now view the Reports through the open IE browser."
Write-Host -ForegroundColor Green "**STEP COMPLETED!"

Nesting dynamic parameters in PowerShell

I am working on a function that will insert a row into a SQL database. It is basically a simple change log to help me track what is changed on my various SQL instances. As part of this, I want to have the following parameters:
Timestamp
Server
Instance
Change
I've got the Timestamp, Change, and Server all figured out, but the Instance is giving me some trouble. The Server parameter is dynamic, as it pulls a list of SQL servers from my inventory. I then want the value of that parameter to be used in another dynamic parameter, which pulls a list of the instances that are on that server (also from my inventory). Here is what I have for the dynamic portion:
DynamicParam {
if (!(Get-Module sqlps)){ Pop-Location; Import-Module sqlps -DisableNameChecking; Push-Location }
$inventoryinstance = 'ServerName'
$newparams = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$server_query = 'SELECT [Name] FROM [ServerInventory].[dbo].[Servers] WHERE [TypeID] = 1 ORDER BY [Name]'
$servers = Invoke-Sqlcmd -serverinstance $inventoryinstance -query $server_query -connectiontimeout 5
# Populate array
$serverlist = #()
foreach ($servername in $servers.Name) {
$serverlist += $servername
}
$attributes = New-Object System.Management.Automation.ParameterAttribute
$attributes.ParameterSetName = "__AllParameterSets"
$attributes.Position = 1
$attributes.Mandatory = $true
$attributes.HelpMessage = "The server the change was made on"
# Server list parameter setup
if ($serverlist){ $servervalidationset = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList $serverlist }
$serverattributes = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
$serverattributes.Add($attributes)
if ($serverlist){ $serverattributes.Add($servervalidationset) }
$serverob = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("Server", [String], $serverattributes)
$newparams.Add("Server", $serverob)
$instance_query = "SELECT [Name] FROM [ServerInventory].[dbo].[SQLInstances] WHERE [ServerID] = (SELECT [ServerID] FROM [ServerInventory].[dbo].[Servers] WHERE [Name] = '$($PSBoundParameters.Server)')"
$instances = Invoke-Sqlcmd -serverinstance $inventoryinstance -query $instance_query -connectiontimeout 5
# Populate array
$instancelist = #()
foreach ($instancename in $instances.Name) {
$instancelist += $instancename
}
$attributes = New-Object System.Management.Automation.ParameterAttribute
$attributes.ParameterSetName = "__AllParameterSets"
$attributes.Position = 2
$attributes.Mandatory = $false
$attributes.HelpMessage = "The instance the change was made on, do not specify for server-level changes"
# Server list parameter setup
if ($instancelist){ $instancevalidationset = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList $instancelist }
$instanceattributes = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
$instanceattributes.Add($attributes)
if ($instancelist){ $instanceattributes.Add($instancevalidationset) }
$instanceob = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("Instance", [String], $instanceattributes)
$newparams.Add("Instance", $instanceob)
return $newparams
}
Everything seems to be working, except the value for the instance variable doesn't autocomplete. Is it possible to use the value of one dynamic parameter to generate another?