I have an Excel file that stores the locations of .sql and .csv and the email addresses to be sent. The field headers are "To" (email recipients), "Subject" (email subjects), "Query" (.sql paths), and "CSV" (.csv paths to be exported). I made a powershell script that will run the queries and then send the exported csv to corresponding email addresses.
It works now without problem but I have no idea how I could modify the script so I can add this function; some recipients don't want to receive multiple emails after the script -> One email with multiple csv results/attachments (so I can group all csv if they are belong to each address). For example, recipient alan#abc.com may receive email of query 1,2,3 (3 csv) according to the excel file. Do you have any suggestion on that? Thanks.
This is my script (it's a for loop and will check if there is any empty result):
#SQL/Mail
$SQLServer = '.\SQLEXPRESS'
$db = 'db'
$smtp = 'smtp.com'
$from = 'test-report#abc.com'
#variable
$var = Import-CSV C:\SQL\Var.csv
$lastmonth = (Get-Date).AddMonths(-1).ToString("MMMM")
$logfilename = $(get-date -f yyyy-MM-dd)
#starts
Start-Transcript -Path C:\SQL\log_$logfilename.txt
for ($i = 0; $i -lt $var.Count; ++$i){
#email subject
$subject = $var.Subject[$i] + ' of '+ $lastmonth
#status
Write-Host "Running" $var.Subject[$i]
#query execution
$result = Invoke-Sqlcmd -InputFile $var.Query[$i] -ServerInstance $SQLServer -Database $db -OutVariable sqlReturn
#check if the result is empty
$is_empty = ($result).count
if($is_empty -eq 0){
Write-Host $var.Subject[$i] "has empty Result"
Send-MailMessage -From $from -To $var.To[$i] -Subject $subject -Body 'Empty' -Priority High -DeliveryNotificationOption OnSuccess, OnFailure -SmtpServer $smtp #send message without attachment
Write-Host "Sending Email"
}
else{
$result | Export-Csv -NoTypeInformation -Path $var.CSV[$i] -Encoding UTF8 #result export to csv
Send-MailMessage -From $from -To $var.To[$i] -Subject $subject -Body $lastmonth -Attachments $var.CSV[$i] -Priority High -DeliveryNotificationOption OnSuccess, OnFailure -SmtpServer $smtp #send with csv
Write-Host "Sending Email"
}
}
#finished loop
Write-Host "Finished"
Stop-Transcript
Related
I have not an Expert in power shell. I Need a script/Approch which handles the below requirement.
I have list of files in a folder and the file Names like below.
001_File.sql
002_File.sql
003_File.sql
004_File.sql
Also, I have a table in sql server which holds the file Name Information.
TableName: Executedfile with a column FileName.
002_File.sql
004_File.sql
My requirement is to read the files which is available in the folder but not in the table.
I have to the Read files only:
001_File.sql
003_File.sql
Now, I Need to Execute these two file in the sequential order under the same Transaction on SQL Server. As I Need to rollback all the transaction if any Error occurred.
As of now I wrote a power shell something below.
$QueryResult = Invoke-Sqlcmd -ServerInstance 'MyServer' -Database 'MyDb' -Query "SELECT DISTINCT FNames from TableName"
Get-ChildItem "E:\Testing\" -Filter *.sql | Sort-Object $_.Name|
Foreach-Object {
$FileFullpath= $_.FullName
Write-Host $FileFullpath
$FileName = $_.Name
Write-Host $FileName
if(!$QueryResult.FName.Contains($FileName))
{
invoke-sqlcmd -inputfile $FileFullpath -serverinstance "servername\serverinstance" -database "mydatabase"
}
}
Please suggest me some script.
Challenges:
How to read the files in Sequential order as it has leading Zeros. Does the Above `Sort-Object $_.Name ' will Sort ?
How to Execute all the list of files under one transaction.
Thanks
Finally I did something like this.
$QueryResult = Invoke-Sqlcmd -ServerInstance 'MyServer' -Database 'MyDb' -Query "SELECT DISTINCT FNames from TableName"
$FullScript = #()
$FullScript += "BEGIN TRANSACTION;"
Get-ChildItem "E:\Testing\" -Filter *.sql | Sort-Object $_.Name|
Foreach-Object {
if(!$QueryResult.FName.Contains($_.Name))
{
$FullScript += Get-Content $_.FullName
}
}
$FullScript += "COMMIT TRANSACTION;"
sqlcmd -S localhost -d test -Q "$FullScript"
Try this...
#get list of filenames from database...
$QueryResult = Invoke-Sqlcmd -ServerInstance 'MyServer' -Database 'MyDB' -Query "SELECT DISTINCT FNames from TableName" | Select-Object -ExpandProperty FileName
#get files from folder whose names are not in $queryresult...
$files = Get-ChildItem -Path E:\Testing -Filter *.sql | ? {(!($QueryResult.Contains($_.BaseName)))} | Sort-Object Name
#get the content of each $file and replace "GO" with empty string, etc...
$queries = #()
foreach ($file in $files) {
$queries += (Get-Content $file.FullName).replace("GO","")
}
#join each query into a single T-SQL statement...
$singleTransaction = $queries -join ";"
#execute statement...
Invoke-Sqlcmd -ServerInstance 'SERVER' -Database 'DB' -Query $singleTransaction
To really achieve a 'single transaction'...you may have to have a consistent input to modify and put into one statement. I am not sure how you will need to do that.
Finally I wrote the Script using SMO Objects to handle the GO Statement and Transactions.
$SqlFilePath = "D:\Roshan\Testing\SQL\"
$serverName = "MyServer"
$databaseName = "MyDB"
$QueryResult = Invoke-Sqlcmd -ServerInstance $serverName -Database $databaseName -Query "SELECT DISTINCT FName from dbo.TableName" -AS DataRows
$connection = new-object system.data.SqlClient.SQLConnection("Data Source=$serverName;Integrated Security=SSPI;Initial Catalog=$databaseName;Connection Timeout=600;Max Pool Size=10");
$Server = new-Object Microsoft.SqlServer.Management.Smo.Server(New-Object Microsoft.SqlServer.Management.Common.ServerConnection($connection))
$script_contents ="SET XACT_ABORT ON
GO
BEGIN TRANSACTION
GO"
Get-ChildItem $SqlFilePath -Filter *.sql| Sort-Object $_.Name|
ForEach-Object {
if(!$QueryResult.FName.Contains($_.Name))
{
Write-Host $_.Name -ForegroundColor Magenta
#[string]$script_contents = Get-Content $_.FullName
$script_contents += [IO.File]::ReadAllText($_.FullName)
#Write-Host $script_contents
#$Server.ConnectionContext.ExecuteNonQuery($script_contents)
}
}
$script_contents+= " COMMIT TRANSACTION;"
$Server.ConnectionContext.ExecuteNonQuery($script_contents)
You can write some thing in your shell script
select filename from tablename; >> file.out
--->002_File.sql
grep -v 'file.out' * >> excludedfile.out
I would like to get all the installed version values of SQL on over 200 different Servers.
The plan is, to have all the Server Names in the ServerListSQLVersions.txt
and to get all the SQL Versions into the CSV.
$Username = ''
$Password = ''
$pass = ConvertTo-SecureString -AsPlainText $Password -Force
$SecureString = $pass
# Users you password securly
$MySecureCreds = New-Object -TypeName
System.Management.Automation.PSCredential -ArgumentList $Username,$SecureString
$Array = #()
##Create a Folder called SQLVersions##
$scriptPath = "C:\Transfer to SV000229\SQL Script"
$server = Get-Content "$scriptPath\ServerListSQLVersions.txt"
$wmiobj = Get-WmiObject -class Win32_product | where Name -like '*SQL*' | Select-Object name,version
function getWMIObject($server, $wmiobj, $MySecureCreds) {
$result = Get-WmiObject $wmiobj -ComputerName $server -Credential $MySecureCreds
#Write-Host "Result: "$result
$Array+= $Result
}
$Array = Export-Csv $scriptpath\output.csv -NoTypeInformation
My output in the CSV is:
Length
0
I used a
foreach($computer in $computers){
instead of the function and gave the information manually.
Also the output was not abled to Export, because i used an = instead of an |
Works now.
I have a Powershell that sends a message to Rabbit-MQ. I've seen the same from BizTalk. The new queue name does not show up on the web interface until a message is retrieved from that queue at least one time. Is this the normal process? I don't understand why it wouldn't show up as soon as data was written to it.
Similar, I seem to be noticing that I can't store records in the queue until it is read once. I'm sending to an exchange with a routing key. If I write a program and send 5 messages there, the queue doesn't show up in RabbitMQ. But once I create a program to read/listen to that queue, from then on it shows up with the count of messages.
Sample Powershell Code:
Import-Module PSRabbitMQ
Set-RabbitMQConfig -ComputerName localhost
$User = "myuser"
#The second command uses the ConvertTo-SecureString cmdlet to create a secure string from a plain text password. The command uses the *AsPlainText* parameter to indicate that the string is plain text and the *Force* parameter to confirm that you understand the risks of using plain text.
$PWord = ConvertTo-SecureString -String "mypassword" -AsPlainText -Force
#The third command uses the New-Object cmdlet to create a **PSCredential** object from the values in the $User and $PWord variables.
$CredRabbit = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $User, $PWord
#Set some common parameters we will always use:
$Params = #{
Credential = $CredRabbit
}
$Exchange = "MyExchange"
$RoutingKey = "NealTest3"
$showDate = Get-Date -DisplayHint Date
$numMessagesPerRun = 5
for ($j=1; $j -le $numMessagesPerRun; $j++)
{
$message = "Hello at date/time= $showDate " + $j
Write-Host "Message = $message"
Send-RabbitMQMessage -Exchange $Exchange -Key $RoutingKey -InputObject $message -vhost base -Persistent #Params
}
$showDate = Get-Date -DisplayHint Date
Write-Host "Ended $showDate"
My Queue: NealTest3 will not show up when I do "rabbitmqctl list_queues -p base" until I run another Powershell to consume at least one message from that queue.
Code from second program to read the queue (left out the same logon info):
Start-RabbitMqListener -Exchange $Exchange `
-Key $QueueName `
-QueueName $QueueName `
-AutoDelete $false `
-vhost base `
#Params | % {
#$req = $_ | ConvertFrom-Json
$req = $_
$counter = $counter + 1
Write-Host $counter + " " + $req
}
It reads the 5 messages I put there, even though the queue in theory didn't exist, according to the list_queues.
Currenly, I am working to setup a powershell job in SQL server to send database mail for some results in a table format. Here is my script:
$SMTPProperties = #{
To = "abc#abc.com.hk","test#test.com"
Cc = "xyz#xyz.com"
From = "test#abc.com.hk"
Subject = "SQL Report Status"
SMTPServer = "192.168.xx.xx"
}
$server = "192.168.xx.xx"
$database = "DBName"
$username = "abc"
$password = "abc124"
$query = "select top 10* from testing"
function ExecuteSqlQuery ($Server, $Database, $query) {
$Connection = New-Object System.Data.SQLClient.SQLConnection
$Connection.ConnectionString = "server='$Server';database='$Database';User ID='$username'; Password='$password';trusted_connection=true;"
$Connection.Open()
$Command = New-Object System.Data.SQLClient.SQLCommand
$Command.Connection = $Connection
$Command.CommandText = $query
$Reader = $Command.ExecuteReader()
$Datatable = New-Object System.Data.DataTable
$Datatable.Load($Reader)
$Connection.Close()
return $Datatable
}
$resultsDataTable = New-Object System.Data.DataTable
$resultsDataTable = ExecuteSqlQuery $Server $Database $query
Send-MailMessage #SMTPProperties -Body $query -BodyAsHTML | Format-Table
A few questions comes:
1. In #SMTPProperties, how can I send to multiple recipients? Solved
2. The script works but in content of the received email, it simply returns
text of the query (select top 10* from testing). It is not the
query result.
3. Is my script correct to output a HTML table in the email
content? If not , how can I change it?
4. How can I run above without provide UID and Password in above script.
Thank you.
For more recipients you can use Cc field
I use this for sending mails for my Powershell scripts.
Send-MailMessage `
-Credential $anonCredentials `
-From FromMailAddress#Domain.com `
-To MainMailAddress#Domain.com `
-Cc "FirstRecipient#Domain.com","SecondRecipient#Domain.om","ThirdRecipient#Domain.com" `
-Subject "Enter your subject" -Body "This is an automated message from the server with some data" `
-SmtpServer 192.168.x.x `
-Attachments "C:\ThedataIwanttosend.rar"
If you try to export your report in an html file or something else and then mail it to those that must receive it? does this solution works for you? if you run your script you have any results?
You are setting $Query to a text and then you never update with something new.
your -body takes $query as text so it is right to get that text as a mail.
Send-MailMessage #SMTPProperties -Body $query -BodyAsHTML | Format-Table
Are you getting the right data from the Function you are using? if yes then you have to put those results in a variable and write that variable as a body.
now you have :
$query = "select top 10* from testing"
Send-MailMessage #SMTPProperties -Body $query -BodyAsHTML | Format-Table
so the mail you are getting gets the -body data from the $query variable that is the text you set on the $query variable.
If you want something else in that mail body you have to save it into the $query variable or create a new variable with the results and then add it to the -Body.
Hope it helps.
I have been trying to find a solution to this problem for quite a while now and nothing I've found seems to work. I am hoping to use this script:
##-----------------------------------------------------##
## PICK AUTH Method ##
##-----------------------------------------------------##
## HARD CODING PSW ##
#$password = ConvertTo-SecureString "xxx" -AsPlainText -Force
#$cred = New-Object System.Management.Automation.PSCredential "xxx#xxx.onmicrosofot.com",$password
## USER PROMPT PSW ##
#$cred = Get-Credential
##-----------------------------------------------------##
## END PICK
##-----------------------------------------------------##
$url = "https://outlook.office365.com/api/v1.0/me/messages"
$date = "2014-11-21"
## Get all messages that have attachments where received date is greater than $date
$messageQuery = "" + $url + "?`$select=Id&`$filter=HasAttachments eq true and DateTimeReceived ge " + $date
$messages = Invoke-RestMethod $messageQuery -Credential $cred
## Loop through each results
foreach ($message in $messages.value)
{
# get attachments and save to file system
$query = $url + "/" + $message.Id + "/attachments"
$attachments = Invoke-RestMethod $query -Credential $cred
# in case of multiple attachments in email
foreach ($attachment in $attachments.value)
{
$attachment.Name
$path = "c:\Temp\" + $attachment.Name
$Content = [System.Convert]::FromBase64String($attachment.ContentBytes)
Set-Content -Path $path -Value $Content -Encoding Byte
}
}
That I found from link - TechNet Office 365 Scripts
I can get the script to work fine, it connects to mailbox and downloads the attachments for emails specified after the date variable. Problem is that I would like to run this on an hourly schedule and delete previously processed emails so that their attachments are not stripped on consecutive runs.
I've tried lots of different commands to delete the email with no success and also tried altering the $messageQuery to include only items within a specific date range but can't get anything working.
Can anyone provide the correct syntax for the delete command in powershell and an example of how to use it in this context please?
Thanks
Rich
UPDATE - I think adding in:
$query2 = $url + "/" + $message.Id
And
Invoke-RestMethod $query2 -Credential $cred -Method Delete
Is getting me closer but I now get operation timed out.
UPDATE - Full code as it stands and as requested:
##-----------------------------------------------------##
## PICK AUTH Method ##
##-----------------------------------------------------##
## HARD CODING PSW ##
$password = ConvertTo-SecureString "password" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential "email#address.co.uk",$password
## USER PROMPT PSW ##
##$cred = Get-Credential
##-----------------------------------------------------##
## END PICK
##-----------------------------------------------------##
#$url = "https://outlook.office365.com/api/v1.0/me/messages"
$url = "https://outlook.office365.com/EWS/ODATA/me/messages"
$today = Get-Date -format yyyy-MM-dd
$date = (Get-Date).AddDays(-2).ToString("yyyy-MM-dd")
## Set date and query
$messageQuery = "" + $url + "?`$select=Id&`$filter=HasAttachments eq true and DateTimeReceived lt " + $today #+ " and DateTimeReceived gt" + $date
$messages = Invoke-RestMethod $messageQuery -Credential $cred
## Loop through each results
foreach ($message in $messages.value)
{
# get attachments and save to file system
$query = $url + "/" + $message.Id + "/attachments"
$query2 = $url + "/" + $message.Id
$attachments = Invoke-RestMethod $query -Credential $cred
# in case of multiple attachments in email
foreach ($attachment in $attachments.value)
{
$attachment.Name
$path = "\\SomePath\" + $attachment.Name
$Content = [System.Convert]::FromBase64String($attachment.ContentBytes)
Set-Content -Path $path -Value $Content -Encoding Byte
}
Invoke-RestMethod $query2 -Credential $cred -Method Delete -TimeoutSec 100
}
#Move pdf files to another folder
#Move-Item '\\SomePath\*.pdf' '\\SomePath'
#Then delete any existing files from directory
#Remove-Item '\\SomePath\*'
I tried to alter the date section to allow for a start and end date so the script could be run once a day, do the extraction but not reprocess attachments from previous days but ran into issues working out the syntax to alter the $messageQuery.
Response from running the script is:
Invoke-RestMethod : The operation has timed out.
At C:\Scripts\EmailExtractServiceContract365.ps1:26 char:13
+ $messages = Invoke-RestMethod $messageQuery -Credential $cred
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Not an answer
Use Fiddler to capture the request. Use the https://outlook.office365.com/api/v1.0/me/messages endpoint. The other one has been deprecated (I think). Post your sanitized request/response here.
Your script is probably not the problem
I got the script to work fine; it finds an email with attachment, saves the attachment, then deletes the email. I'm not sure what is going on in your environment until I see the requests (remove the auth headers if you post the request). I only changed the $today value to capture emails with attachments today.
$password = ConvertTo-SecureString "password" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential "user#contoso.com",$password
$url = "https://outlook.office365.com/api/v1.0/me/messages"
$today = (Get-Date).AddDays(1).ToString("yyyy-MM-dd")
$date = (Get-Date).AddDays(-2).ToString("yyyy-MM-dd")
## Set date and query
$messageQuery = "" + $url + "?`$select=Id&`$filter=HasAttachments eq true and DateTimeReceived lt " + $today #+ " and DateTimeReceived gt" + $date
$messages = Invoke-RestMethod $messageQuery -Credential $cred
## Loop through each results
foreach ($message in $messages.value)
{
# get attachments and save to file system
$query = $url + "/" + $message.Id + "/attachments"
$query2 = $url + "/" + $message.Id
$attachments = Invoke-RestMethod $query -Credential $cred
# in case of multiple attachments in email
foreach ($attachment in $attachments.value)
{
$attachment.Name
$path = "C:\temp\" + $attachment.Name
$Content = [System.Convert]::FromBase64String($attachment.ContentBytes)
Set-Content -Path $path -Value $Content -Encoding Byte
}
Invoke-RestMethod $query2 -Credential $cred -Method Delete -TimeoutSec 100
}
REQUEST
Here's what the request and response should look like to delete the email.
DELETE https://outlook.office365.com/api/v1.0/me/messages/AAMkADBjYXXXXXXXXXX= HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.0.10586.0
Authorization: XXXXXXXXXXXXXXXXXXXXXXXXX
Host: outlook.office365.com
Cookie: ClientId=CQA
Content-Length: 0
RESPONSE
HTTP/1.1 204 No Content