I am working on downloading email bodies and their attachments. I want to make sure that each sender has numbers attached to the filename to differentiate from other documents. The counter should start per each sender. For example
Sender1
Sender1-Emailbody-0
Sender1-pdf attachment-1
Sender1-jpg attachment -2
Sender2
Sender2-Emailbody-0
Sender2-pdf attachment -1
Sender2-jpg attachment -2
I am not sure of how to about it. Any help, please. Thanks
$emailQuery1 = $url + "?`$select=Id,Sender,DateTimeReceived&`$filter=HasAttachments eq true and DateTimeReceived ge " + $datetime
$emails = Invoke-RestMethod -Uri $emailQuery1 -Headers $head
## Loop through each results
Foreach ($email in $emails.value)
{
#get attachments and save to file system
$query = $url + "/" + $email.Id + "/attachments"
$body = $url + "/" + $email.Id + "/body"
$receivedtime = "/" + $email.id + "/DateTimeReceived"
$attachments = Invoke-RestMethod -Uri $query -Headers $head
$emailbody = Invoke-RestMethod -Uri $body -Headers $head
Foreach($em in $emailbody)
{
$bodyitems = $em.content
Foreach ($attachment in $attachments.value)
{
$attachment.Name
$path = $DFSPath + $recvtime +"_" + $email.Sender.EmailAddress.Address + "_" + $attachment.Name
$Content = [System.Convert]::FromBase64String($attachment.ContentBytes)
$recvtime = $email.DateTimeReceived | get-date -Format yyyyMMddHHmmss
$bodyitems | out-file -FilePath ($DFSPath +$recvtime +"_" + $email.Sender.EmailAddress.Address`
+"_emailbody-" +$Counter+".html")
Set-Content -Path $path -Value $Content -Encoding Byte
}
}
}
I recommend using a counting variable that gets incremented right before you save the file. Set it to 0 at the beginning of the foreach email loop, $x=0, and then put $x++ right before the Out-File command. You can then append it to the file name when saving the file.
Related
I've been trying to build out some code in PowerShell (PowerShell 6 Core works, the following code didn't work in PowerShell 5),
$Token = ' Token Here ';
$Headers = #{
Method = 'POST'
Uri = ' URL Here '
Headers = #{Authorization = "Bearer $Token" }
}
$bodyHere = #{
roomId = ' ChatSpace ID Here '
text = ' Some Random Text '
files = Get-Item -Path 'c:\test.png'
}
try {
Invoke-RestMethod #Headers -Form $bodyHere
} catch [System.Net.WebException] {
Write-Error $_.Exception.ToString()
throw $_
}
This works well and can upload the file, however I need to also add Content-Type: "image/png" to my Get-Item item - is there an easy way to do this?
I've also trying to do it another way by building a multipart form I've seen someone else use on StackOverflow, but I get another issue now where I can't add to the multipart form when I attempt to use Add-Member or use any other method to append to the form.
$Token = ' Token Here ';
$Headers = #{
Method = 'POST'
Uri = ' URL Here '
Headers = #{Authorization = "Bearer $Token" }
}
$bodyLines = #{
roomId = ' ChatSpace ID Here '
text = ' Random Text here '
}
$FilePath = 'c:\test.png'
$FieldName = 'files'
$ContentType = 'image/png'
$FileStream = [System.IO.FileStream]::New($filePath, [System.IO.FileMode]::Open)
$FileHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::New('form-data')
$FileHeader.Name = $FieldName
$FileHeader.FileName = Split-Path -Leaf $FilePath
$FileContent = [System.Net.Http.StreamContent]::New($FileStream)
$FileContent.Headers.ContentDisposition = $FileHeader
$FileContent.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse($ContentType)
$MultipartContent = [System.Net.Http.MultipartFormDataContent]::New()
$MultipartContent.Add($FileContent)
$MultipartContent.Add($bodyLines) # this doesn't work as I'm building a multipart form
$MultipartContent | Add-Member -Name "headers" -Value $bodyLines -MemberType NoteProperty # I convert this to JSON and use write-host but it doesn't append I've also tried $MultipartContent.headers and it still doesn't append
try {
# Invoke-RestMethod #Headers -Body $MultipartContent
Invoke-WebRequest #Headers -Body $bodyLines
} catch [System.Net.WebException] {
Write-Error $_.Exception.ToString()
throw $_
}
Any help on either how to build a multipart form with both a file upload and other parameters or tacking on content-type to a Get-Item call, would be super appreciated. Just to give you an idea of what the code looks like in Python (much easier):
import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder
m = MultipartEncoder({'roomId': ' ChatSpace ID Here ',
'text': ' Random Text Here ',
'files': ('example.png', open('example.png', 'rb'),
'image/png')})
r = requests.post(' URL Here ', data=m,
headers={'Authorization': 'Bearer TokenHere',
'Content-Type': m.content_type})
print r.text
I think you want the content of the file instead of the file information. Try something like files = (Get-Content 'c:\test.png') in your script. If you just want the path to file you don't need to use Get-Item at all.
If you are uploading a .PNG file this may not work if the bytes include control characters that fool the server side parsing of the input.
I'm currently trying to send a mail using an automated Powershell script using the GraphAPI from Microsoft.
Here the full story : I need to check every 15 min the mail received on an Office 365 mailbox, get a specific file attachment (filter by is name), download the file, and re-send it with a new mail.
Everything working but I canno't send the new mail with the attachment ( Works without the attachment).
Here my code :
$date = (get-date).ToUniversalTime().AddMinutes(-15).ToString("yyyy-MM-ddTHH:mm:ssZ")
$Uri = "https://graph.microsoft.com/v1.0/me/messages"
$UriSend = "https://graph.microsoft.com/v1.0/me/sendMail"
$filter = "?`$select=Id,ReceivedDateTime&`$filter=HasAttachments eq true and ReceivedDateTime ge " + $date
$url = $Uri + $filter
$Result = (Invoke-RestMethod -Method Get -Headers $requestheader -Uri $url)
if($Result.value){
## Loop through each results
foreach ($message in $Result.value)
{
# get attachments and save to file system
$query = $Uri + "/" + $message.Id + "/attachments"
$attachments = (Invoke-RestMethod -Method Get -Headers $requestheader -Uri $query)
# in case of multiple attachments in email
foreach ($attachment in $attachments.value)
{
$patternPDF = "FAX AR document\.pdf$"
if($attachment.Name -match $patternPDF)
{
$name = $message.ReceivedDateTime -replace "T","-"
$name = $name -replace "Z",""
$name = $name -replace ":","-"
$path = "c:\Temp\" + $name + ".pdf"
# Creation of the PDF file
$Content = [System.Convert]::FromBase64String($attachment.ContentBytes)
Set-Content -Path $path -Value $Content -Encoding Byte
$file = Get-Content $path -Encoding Byte -ReadCount 0
#Send File by Mail
$body =
#"
{
"message" : {
"subject": "AR Fax",
"body" : {
"contentType": "Text",
"content": "Accusé Fax"
},
"toRecipients": [
{
"emailAddress" : {
"address" : "mail#domain.com"
}
}
],
"attachments":[
{
"##odata.type":"#microsoft.graph.fileAttachment",
"name":"Fax_AR.pdf",
"contentType":"application/pdf",
"contentBytes":"$file"
}
]
},
"saveToSentItems": "true"
}
"#
# Invokes the request
Invoke-RestMethod -Headers $requestheader -Uri $uriSend -Method Post -Body $body
I'm not very good in powershell (or in code generally) so please be indulgent^^
EDIT : I've find another way to do what i want (I print the file now)
I've find another way to do what i want (I print the file now)
So I'm closing the topic.
I have created a script in PowerShell 5.1 that retrieves mail messages not older that one day with 'report' as a subject and save their attachments into local drive. The problem is that in the production environment I have only Powershell 2.0. I am using Invoke-RestMethod in my code like this:
$url = "https://outlook.office365.com/api/v1.0/me/messages"
$date = (get-date).AddDays(-1).ToString("yyyy-MM-dd")
$subject = "'report'"
$messageQuery = "" + $url + "?`$select=Id&`$filter=HasAttachments eq true and DateTimeReceived ge " + $date + " and Subject eq " + $subject
$messages = Invoke-RestMethod $messageQuery -Credential $cred
foreach ($message in $messages.value)
{
$query = $url + "/" + $message.Id + "/attachments"
$attachments = Invoke-RestMethod $query -Credential $cred
foreach ($attachment in $attachments.value)
{
$attachment.Name
# SAVE ATTACHMENT CODE HERE
}
}
Is there a simple way to convert the code in order to be suitable for PowerShell 2.0?
Invoke-WebRequest
This command is basically the same as Invoke-RestMethod except in how it handles the data after it receives it. You are going to have to make some small modifications on how you parse your data.
I'll wager you are receiving JSON data so you would just run your Invoke-WebRequest command and pipe it to ConvertFrom-JSON and assign the results to a var. This will let you then do something like $x.messages | % { $_ }
You will need to implement this converter in 2.0. You can copy and paste from: PowerShell 2.0 ConvertFrom-Json and ConvertTo-Json implementation
XML is supported in PS 2.0 natively.
Need to delete collection in my automation process.
Trying to execute script below. Get operations working fine, but Delete operation failed with "(401) Unathorized" error. It is strange cause Delete collection do not need additional headers. Can someone give a hint, what is wrong?
$accountName = 'someaccountname'
$connectionKey = 'masterkey'
$collectionName = 'mycollection'
$databaseName = 'mydatabase'
function GetKey([System.String]$Verb = '',[System.String]$ResourceId = '',
[System.String]$ResourceType = '',[System.String]$Date = '',[System.String]$masterKey = '') {
$keyBytes = [System.Convert]::FromBase64String($masterKey)
$text = #($Verb.ToLowerInvariant() + "`n" + $ResourceType.ToLowerInvariant() + "`n" + $ResourceId + "`n" + $Date.ToLowerInvariant() + "`n" + "`n")
$body =[Text.Encoding]::UTF8.GetBytes($text)
$hmacsha = new-object -TypeName System.Security.Cryptography.HMACSHA256 -ArgumentList (,$keyBytes)
$hash = $hmacsha.ComputeHash($body)
$signature = [System.Convert]::ToBase64String($hash)
[System.Web.HttpUtility]::UrlEncode($('type=master&ver=1.0&sig=' + $signature))
}
function BuildHeaders([string]$action = "get",[string]$resType, [string]$resourceId){
$authz = GetKey -Verb $action -ResourceType $resType -ResourceId $resourceId -Date $apiDate -masterKey $connectionKey
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", $authz)
$headers.Add("x-ms-version", '2015-12-16')
$headers.Add("x-ms-date", $apiDate)
$headers
}
function GetUTDate() {
$date = get-date
$date = $date.ToUniversalTime();
return $date.ToString("r", [System.Globalization.CultureInfo]::InvariantCulture);
}
function GetDatabases() {
$uri = $rootUri + "/dbs"
$hdr = BuildHeaders -resType dbs
$response = Invoke-RestMethod -Uri $uri -Method Get -Headers $hdr
$response.Databases
Write-Host ("Found " + $Response.Databases.Count + " Database(s)")
}
function GetCollections([string]$dbname){
$uri = $rootUri + "/" + $dbname + "/colls"
$hdr = BuildHeaders -resType colls -resourceId $dbname
$response = Invoke-RestMethod -Uri $uri -Method Get -Headers $hdr
$response.DocumentCollections
Write-Host ("Found " + $Response.DocumentCollections.Count + " DocumentCollection(s)")
}
function DeleteCollection([string]$dbname){
$uri = $rootUri + "/" + $dbname + "/colls" + "/" + $collectionName
$hdrs = BuildHeaders -action DELETE -resType colls -resourceId $collectionName
$response = Invoke-RestMethod -Uri $uri -Method Delete -Headers $hdrs
Write-Host "DELETE $uri"
}
$rootUri = "https://" + $accountName + ".documents.azure.com"
write-host ("Root URI is " + $rootUri)
#validate arguments
$apiDate = GetUTDate
$db = GetDatabases | where { $_.id -eq $databaseName }
if ($db -eq $null) {
write-error "Could not find database in account"
return
}
$dbname = "dbs/" + $databaseName
$collection = GetCollections -dbname $dbname | where { $_.id -eq $collectionName }
if($collection -eq $null){
write-error "Could not find collection in database"
return
}
Write-Host
$Delete = DeleteCollection -dbname $dbname | where { $_.id -eq $collectionName }
Normally, either the Authorization or x-ms-date header is not set (or the Authorization header with an invalid authorization token), 401 unauthorized error will be returned. I use fiddler to capture the request and check the response, and I find both Authorization and x-ms-date header are set, so it seems that the Authorization header is set to an invalid authorization token. Based on your code, I do some changes, and the function could work fine on my side.
function DeleteCollection([string]$dbname){
$uri = $rootUri + "/" + $dbname + "/colls" + "/" + $collectionName
$collectionName = $dbname + "/colls" + "/" + $collectionName
$hdrs = BuildHeaders -action DELETE -resType colls -resourceId $collectionName
#Write-Host "resourceId $collectionName"
$response = Invoke-RestMethod -Uri $uri -Method Delete -Headers $hdrs
Write-Host "resource $collectionName"
Write-Host "DELETE $uri"
}
The $collectionName should be dbs/{yourdbname}/colls/{yourcollectionname}
This can happen if there are pending operations. Either retry until it succeeds or add a delay before deleting. For us, a half second is enough to assure we never see this again.
I use Invoke-RestMethod to get data from web and export to csv. I have a need to insert a calculation field and remove two fields. My following code yield nothing, why?
$request = "www.oanda.com/rates/api/v1/rates/USD.csv?quote=CNY"e=EUR"e=GBP"e=KRW&fields=averages&api_key=Yv0C4DZ2rFOybbnGwzj7niFh"
Invoke-RestMethod -Method Get -Uri $url -OutFile "C:\curr.csv"
# remove header
$currencys = Invoke-RestMethod -Method Get -Uri $request
$currencys = $currencys[1..($currencys.count - 1)]
$(
write-host "base,","curr,","date,","bid,","ask"
foreach($currency in $currencys){
$base = $($currency.base_currency)
$curr = $($currency.currency)
$date = $($currency.date)
$bid = $($currency.bid)
$ask = $($currency.ask)
$average = ($bid + $ask)/2.0
$line = $curr + "," + $date + "," + $bid + "," + $average
write-host $line
}
) | export-csv test.csv