Post message to MS Teams via powershell - json

This is my first post here ,i hope i will get some help.
I have created a script in powershell for checking the expiry date of services principal in azure.The script itself works fine but now i want to post the output of script to MS Teams instead of generating a result file.
This is a piece of code.The result.list contains the output and i am loading it to $Body
$Body = get-content -Path .\result.list
$JSONBody = [PSCustomObject][Ordered]#{
"#type" = "text/plain"
"#context" = "http://schema.org/extensions"
"summary" = "Incoming Alert Test Message!"
"themeColor" = '0078D7'
"title" = "Incoming Alert Test Message!"
"text" = "$Body"
}
$TeamMessageBody = ConvertTo-Json $JSONBody -Depth 100
$parameters = #{
"URI" = '<uri>'
"Body" = $TeamMessageBody
"ContentType" = 'application/json'
}
Invoke-RestMethod #parameters
This i how the result.list looks like.E.g for a service-principal
DisplayName : sp-acr-ldl-pull
ObjectId : ***********
ApplicationId : ***********
KeyId : ***********
Type : Password
StartDate : 6/23/2020 2:29:20 PM
EndDate : 6/23/2021 2:29:16 PM
Status : Expired
But in Teams it is not human readable because the body is not passed correctly
DisplayName: sp-acr-ldl-pull ObjectId: ********** ApplicationId : ********** KeyId: *********** Type: Password StartDate: 6/23/2020 2:29:20PM EndDate
How can i format $JSONBody to get the same output in Teams same in result.list?

Basically, the problem is the line breaks, but you've got two related problems:
You're reading the contents of the file directly using Get-Content. By default in PowerShell this will return not a single string but rather an array of strings, one item for each line in the file. To get the contents as a single raw string, add the -Raw parameter at the end of the command.
By default, Teams is simply displaying your text without any formatting (including line breaks). If you want to send explicit line breaks, you need to tell Teams to do so, and you can do this using html "" tags (Teams message bodies support a limited subset of html). To do this, you can replace the line breaks in the $body variable with html <br> tags, like this: $Body = $Body.Replace("`r`n", "<br>").
Note that you're missing the "Method" parameter on your Invoke-RestMethod command.
Here is final working code, including the above:
$Body = get-content -Path "C:\temp\result.list" -Raw
$Body = $Body.Replace("`r`n", "<br>")
$JSONBody = [PSCustomObject][Ordered]#{
"#type" = "text/plain"
"#context" = "http://schema.org/extensions"
"summary" = "Incoming Alert Test Message!"
"themeColor" = '0078D7'
"title" = "Incoming Alert Test Message!"
"text" = "$Body"
}
$TeamMessageBody = ConvertTo-Json $JSONBody -Depth 100
$parameters = #{
"URI" = '<uri>'
"Body" = $TeamMessageBody
"ContentType" = 'application/json'
"Method" = "POST"
}
Invoke-RestMethod #parameters
So that will at least give you line breaks. If you want to preserve the "Table" style formatting, then I'd suggest using Adaptive Cards rather, perhaps something like the FactSet. You can learn how to send Adaptive Cards here, and here is a sample card using a FactSet, but you'd need to loop through the lines and build up the FactSet and the Adaptive Card, so hopefully the earlier solution is sufficient.

Related

MS Teams alert, triggered via webhook and PS script, does not want to parse the text on multiple lines

I'm trying to create a PS script that is supposed to post MS Teams alerts via webhooks, regarding some metrics. The current solution that I have almost made work is via a PSCustomObject, which is afterwards converted to JSON and used as the body of the alert. The below is the current code that I am using:
$JSONBody = [PSCustomObject][Ordered] #{
"#type" = "MessageCard"
"title" = "Alert Title"
"text" = "Alert 1: $alert1CountVariable
Alert 2: $alert2CountVariable
Alert 3: $alert3CountVariable"
}
$TeamsMessageBody = ConvertTo-Json $JSONBody -Depth 100
$parameters = #{
"URI" = '<Teams Webhook URI>'
"Method" = 'POST'
"Body" = $TeamsMessageBody
"ContentType" = 'application/json'
}
Invoke-RestMethod #parameters
Everything works as needed, but as you can see the text parameter within the PSCustomObject is supposed to parse the 3 alerts on 3 separate lines, which does not seem to happen whatever I try. I tried inserting the \n and \r operators (also tried \n and \r) and nothing works this far.
Another working method that I have is the following:
$Body = '{"text": "Alert 1: ' + $alert1CountVariable +' Alert 2: ' + $alert2CountVariable +' Alert 3: ' + $alert3CountVariable +'"}'
$TeamsUrl = '<Teams Webhook URI>'
Invoke-WebRequest -Uri $TeamsUrl -Method Post -Body $Body -ContentType "application/json"
However, this also does not fully satisfy the criteria, as it still does not display the alets on separate lines and there is no title here.
Any advise on how I can make any of these two options work?
The teams interface seems to accept HTML:
Please see your script adjusted below:
$JSONBody = [PSCustomObject][Ordered] #{
"#type" = "MessageCard"
"title" = "Alert Title"
"text" = "Alert 1: $alert1CountVariable <br>
Alert 2: $alert2CountVariable <br>
Alert 3: $alert3CountVariable"
}
$TeamsMessageBody = ConvertTo-Json $JSONBody -Depth 100
$parameters = #{
"URI" = '<Teams Webhook URI>'
"Method" = 'POST'
"Body" = $TeamsMessageBody
"ContentType" = 'application/json'
}
Invoke-RestMethod #parameters
I have inserted <br> at the end of each line which is a line break in HTML.

Powershell script Missingtypename

I am trying to make some API calls through powershell using this documentation
https://octopus.com/blog/manually-push-build-information-to-octopus
$Headers = #{"X-Octopus-ApiKey"=$MYKEY"}
$jsonBody = #{
PackageId = "Test"
Version = "1.1.1"
OctopusBuildInformation =
#{
BuildEnvironment = "Jenkins"
BuildNumber = "1"
BuildURL = "myURL"
VcsCommitNumber = "250"
VcsType = "Git"
Commits = [{Id = "250" LinkUrl = "gitUrl" Comment = "someComment"}]
VcsRoot = "gitUrl"
}
} | ConvertTo-Json -Depth 5
Invoke-RestMethod -Method Post -Uri "http://alfadeployment/api/build-information" -Headers $Headers -Body $jsonBody
However, since I added the commits array, I am getting the following error:
Missing type name after '['.
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : MissingTypename
PowerShell arrays aren't enclosed in [...] - the latter is used for type literals, i.e. to refer to .NET types - hence the error message.
You may - but don't have to - enclose PowerShell arrays in #(...), the array-subexpression operator; using ,, the array constructor operator is enough, though #(...) can be helpful for visual clarity, and to create an empty (#()) or single-element arrays (#('foo')), which is perhaps clearer than the unary use of , (, 'foo')).
Additionally, the array element in your case must be a hashtable too (#{ ... }, not { ... }, the latter is a script block), and, given that the property definitions are on the same line, they must be ;-separated.
To put it all together:
Commits = #(
#{ Id = "250"; LinkUrl = "gitUrl"; Comment = "someComment" }
)

smartsheet api update rows error 1004. you are not authorized

I am trying to use powershell and the API to update a cell, but I get an error: 1004 You are not authorized to perform this action. I've elided the IDs . . .
I have a 30 day trial. Do I need to buy in to some level of support before I can use the API to do updates or what? The sheet is on my account and I am the owner. Is Write Sheets role not included in Owner?
I am using powershell 5.1 to make the http request – here is my script. It first gets the correct sheet ID, then puts the rows and columns I am interested in into hashes, and then tries to update the sheet, but although I am the owner, I am unauthorized.
# access account of jjj.mmm#ddd.com
$apiKey = "3ccfgk..."
$url = "https://api.smartsheet.com/2.0/sheets/"
$get_headers = #{"Authorization" = "Bearer " + $apiKey}
$put_headers = #{"Authorization" = "Bearer " + $apiKey, "Content-Type: application/json" }
# get all the sheets
$response = Invoke-RestMethod -Uri $url -Headers $get_headers
$rd = $response.data
$json = ConvertTo-Json $rd
# find the Forecast sheet
$sheet_id = $rd | Where {$_.name -eq "Forecast Intermediary Sheet"} | Select -ExpandProperty id
"Sheet ID: $sheet_id"
#get the Forecast sheet
$surl = "$url$sheet_id"
$surl
$response = Invoke-RestMethod -Uri $surl -Headers $get_headers
$response | Format-List
# iterate over the rows and get the active ones
$active_rows = #{}
foreach ($row in $response.rows) {
if ($row.rowNumber -ge 14) {
if ($row.cells[2].value -Match "Active") {
$active_rows.Add($row.cells[2].value, $row.id)
}
}
}
# get the col_ids by date
$date_cols = #{}
foreach ($c in $response.columns) {
if ($c.title -as [datetime]) {
$d = $c.title -replace "/20", "/"
$ds = [datetime]::parseexact($d, 'mm/dd/yy', $null)
$date_cols.Add($ds.Tostring("yyyy-mm-dd"), $c.id)
}
}
"UPDATE:"
$rowid = $active_rows["SSL PS Active"]
$colid = $date_cols["2017-11-01"]
"row: $rowid, col: $colid"
$json = '[{ "id": "'+$rowid+'", "cells": [{"columnId": "'+$colid+'","value": "23"} }]'
"JSON: $json"
$purl = "$surl/rows"
"PUT URL: $purl"
$r = Invoke-RestMethod -Method "PUT" -uri $purl -Headers $put_headers -Body $json
$r
Below is the output of the script. The error seems to indicate it is syntactically correct, but OWNER is insufficient permissions.
Sheet ID: 5724...
https://api.smartsheet.com/2.0/sheets/5724....
UPDATE:
row: 5668..., col: 5118...
JSON: [{ "id": "5668...", "cells": [{"columnId": "5118...","value": "23"} }]
PUT URL: https://api.smartsheet.com/2.0/sheets/5724.../rows
Invoke-RestMethod : {
"errorCode" : 1004,
"message" : "You are not authorized to perform this action.",
"refId" : "14a7o8lu9sfyj"
}
At \\winfiles\jmoore\powershell\ss_api.ps1:58 char:6
+ $r = Invoke-RestMethod -Method "PUT" -uri $purl -Headers $put_headers ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod],
WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodComand
Here is the meta data of my sheet
Sheet ID: 5724...
https://api.smartsheet.com/2.0/sheets/5724....
id : 5724...
name : Forecast Intermediary Sheet
version : 2
totalRowCount : 37
accessLevel : OWNER
effectiveAttachmentOptions : {GOOGLE_DRIVE, DROPBOX, ONEDRIVE, EGNYTE...}
ganttEnabled : False
dependenciesEnabled : False
resourceManagementEnabled : False
cellImageUploadEnabled : True
userSettings : #{criticalPathEnabled=False; displaySummaryTasks=True}
permalink : https://app.smartsheet.com/b/home?lx=LCTEj6F0xWNKWWxFUuLH0w
createdAt : 2017-11-22T22:34:51Z
modifiedAt : 2017-11-22T22:34:51Z
columns : {#{id=6985...; index=0; title=Column1; type=TEXT_NUMBER; primary=True;
validation=False; width=64}, #{id=1356...; index=1; title=Column2;
type=PICKLIST; options=System.Object[]; validation=False; width=64},
#{id=5859...; index=2; title=Column3; type=TEXT_NUMBER; validation=False;
....
You should be able to make API calls using an API Access Token that's owned by a trial Smartsheet account, and you can most certainly update a sheet that you own using an API Access Token you own.
I'd suspect that the 1004 error response is being caused by the fact that the contents of $put_headers isn't formatted properly. i.e., Smartsheet isn't able to accurately parse the headers you're attempting to specify in $put_headers in order to identify the Authorization header and read its value. When Smartsheet doesn't see the Authorization header in an inbound API request, it'll respond with the 1004 You are not authorized to perform this action. error response.
To troubleshoot this issue, I'd suggest that you use a tool like Fiddler to examine the outbound "Update Rows" request, paying special attention to what headers are present in the request. Then, if you discover that the Authorization header is absent from the request, you'll need to figure out how to specify multiple request headers in PowerShell, and update your code accordingly re how you're setting the value of $put_headers.
Update (adding PowerShell code):
I'm no PowerShell expert, but you might try replacing this line:
$put_headers = #{"Authorization" = "Bearer " + $apiKey, "Content-Type: application/json"}
With these lines instead:
$put_headers = #{}
$put_headers.Add("Authorization", "Bearer " + $apiKey)
$put_headers.Add("Content-Type", "application/json")
Thanks #kim-brandl
Or if you want to build the headers in a single command, you need to use syntax like this:
$put_headers = #{"Authorization" = "Bearer " + $apiKey; "Content-Type" = "application/json" }
Note also that your json payload has a few problems including:
Unmatched brackets
Extra square brackets on the outside (the body is an object, not an array)
Column and row ids must be numbers, not strings
I suggest you test the payload in a tool like postman first.

Dynamic Json variable to feed Invoke-Restmethod

I was able to pass the dynamic json object if the object creation code is in single line.
Invoke-RestMethod -ContentType "application/json" -Method Post -Body '{ "name" : "azurefunctionapp2email", "appname": "Applicationnamehere", "requestedBy" : "requestedby", "reqdate" : "requestdate", "status" : "Successfully Deployed", "AppsCount" : "2" }' `
-Uri “https://implementurihere"
Since the dynamic JSON object in real world need be longer, I seperated created with new line and referenced in the above as below. But new line shift causes the json to break. I tried to pipe to ConvertTo-Json function and then found the output to hold '`\r\n' getting introduced:
$body = '{ "name" : "azurefunctionapp2email", `
"appname": "Applicationnamehere", `
"requestedBy" : "requestedby", `
"reqdate" : "requestdate",
"status" : "Successfully Deployed",
"AppsCount" : "2" }' `
Invoke-RestMethod -ContentType "application/json" -Method Post -Body $body `
-Uri “https://implementurihere"
Note: The above works if the $body is single line.
How to approach in such scenarios where we create a dynamic json , long file and feed?
Your example doesn't work because the last line containing a backtick which you have to omit.
You could use a here string to define your JSON so you don't need to seperate each line by a backtick:
$body =
#'
{ "name" : "azurefunctionapp2email",
"appname": "Applicationnamehere",
"requestedBy" : "requestedby",
"reqdate" : "requestdate",
"status" : "Successfully Deployed",
"AppsCount" : "2" }
'#
You could also consider to use a PowerShell hashtable to define your object which will allows you to use variables without the need of a format string:
$bodyObject = #{
name = 'azurefunctionapp2email'
appname = 'Applicationnamehere'
requestedBy = 'requestedby'
reqdate = 'requestdate'
status = 'Successfully Deployed'
AppsCount = '2'
}
$bodyObject | ConvertTo-Json

Accessing data retuned by PowerShell Import-CSV

I am trying to write a PowerShell script that uses a CSV file as input that will turn off the clutter feature in Office 365. The CSV file has only 1 column and that has the 2 target email addresses that I am using for testing. When I run this script with a read-host line and enter a valid email address it works with no errors. When I use the CSV file errors follow.
Import-Module MSOnline
$LiveCred = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/PowerShell -Credential $LiveCred -Authentication Basic -AllowRedirection
Import-PSSession -allowclobber $Session
Connect-MsolService -Credential $LiveCred
cd c:\scripts
Write-Host "This tool removes the Clutter feature from O365 "
$Clutter = Import-Csv .\Clutteroff.csv
foreach ($user in $Clutter){
Set-Clutter -Identity $User -Enable $false
}
When I run this I get the following error :
Cannot process argument transformation on parameter 'Identity'. Cannot convert value "#{UserID=xxxxx#myCompany.com}" to type "Microsoft.Exchange.Configuration.Tasks.MailboxIdParameter". Error: "Cannot
convert hashtable to an object of the following type: Microsoft.Exchange.Configuration.Tasks.MailboxIdParameter. Hashtable-to-Object conversion is not supported in restricted language mode or a Data
section."
+ CategoryInfo : InvalidData: (:) [Set-Clutter], ParameterBindin...mationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,Set-Clutter
+ PSComputerName : ps.outlook.com
Any help would be appreciated and explanations will get extra credit :)
CSV file = User, XXXX#MyCompany.com, YYYY#MyCompany.com
Email addresses are valid.
Putting all of the items in one line like that is not going to work well with Import-CSV. Import-CSV is suited to a table structure (columns and rows), whereas you are just using a comma-separated list (one row, with an unknown number of columns). If in fact you do have the items on different lines, then please correct the question and I'll change the answer.
To work with the data from a file formatted like that, I would just split it into an ArrayList, and remove the first item because it is "User" and not not an email address:
[System.Collections.ArrayList]$Clutter = (get-content .\Clutteroff.csv).split(",")
$Clutter.RemoveAt(0)
Then you can iterate through the array:
foreach ($user in $Clutter){
$address = $user.trim()
{Set-Clutter -Identity $address -Enable $false}
}
For the extra credit, $user in your script was returning a row of key/value pairs to represent columns (keys) and the data in the columns (values). Your error message shows #{UserID=xxxxx#myCompany.com}, so to return just the email address you would use $user.UserID to return the value for UserID.
i GOT THIS WORKING TO PULL FROM CSV SO ONLY THOSE USERS ARE MODIFIED!! SORRY FOR THE CAPS BUT I AM A TOTAL NOOB AND I COULDNT BELIEVE I GOT THIS TO WORK!!! I am beyond STOKED!! :)
the csv requires no headers, just the email address of the users you want to modify in one column
$Clutter = (Get-Content "pathofyourcsv.csv")
foreach ($User in $Clutter) {
$address = $User
Get-Mailbox -Identity $User | Set-Clutter -Enable $false}