sendgrid send email to multiple recipients in Azure function failed - c#

(For the complete version of this question, please refer to
https://social.msdn.microsoft.com/Forums/en-US/20bb5b37-82af-4cf3-8a59-04e5f19572bc/send-email-to-multiple-recipients-using-sendgrid-failure?forum=AzureFunctions)
Sending to single recipient was successful. But found no way to send to multiple recipients or CC/BCC in Azure function.
Tried several formats including
{ "to": [{ "email": ["john.doe#example.com", "sendgridtesting#gmail.com" ] }] }
It seemed to be the limit from azure function. But not sure where goes wrong yet. Please refer to the "bindings" below,
{
"bindings": [
{
"name": "telemetryEvent",
"type": "serviceBusTrigger",
"direction": "in",
"queueName": "threshold-email-queue",
"connection": "RootManageSharedAccessKey_SERVICEBUS",
"accessRights": "Manage"
},
{
"type": "sendGrid",
"name": "$return",
"apiKey": "SendGridKey",
"direction": "out",
"from": "ABC#sample.com",
"to": [{
"email": ["test1#sample1.com", "test2#sample2.com" ]
}]
}
],
"disabled": false
}

I did my example with a HTTP trigger, but based on this you'll be able to make it work with Service Bus trigger.
my function.json:
{
"bindings": [
{
"authLevel": "function",
"name": "req",
"type": "httpTrigger",
"direction": "in",
"methods": [
"get",
"post"
]
},
{
"type": "sendGrid",
"name": "mails",
"apiKey": "MySendGridKey",
"direction": "out",
"from":"samples#functions.com"
}
],
"disabled": false
}
My run.csx:
#r "SendGrid"
using System;
using System.Net;
using SendGrid.Helpers.Mail;
public static HttpResponseMessage Run(HttpRequestMessage req, TraceWriter log, ICollector<Mail> mails)
{
log.Info("C# HTTP trigger function processed a request.");
Mail message = new Mail()
{
Subject = $"Hello world from the SendGrid C#!"
};
var personalization = new Personalization();
personalization.AddTo(new Email("foo#bar.com"));
personalization.AddTo(new Email("foo2#bar.com"));
// you can add some more recipients here
Content content = new Content
{
Type = "text/plain",
Value = $"Hello world!"
};
message.AddContent(content);
message.AddPersonalization(personalization);
mails.Add(message);
return null;
}
I used this source to build my example:
Azure Function SendGrid

Thanks to Tamás Huj, the function can do the job now. So I post the solution in detail for others reference.
{
"bindings": [
{
"name": "telemetryEvent",
"type": "serviceBusTrigger",
"direction": "in",
"queueName": "threshold-email-queue",
"connection": "RootManageSharedAccessKey_SERVICEBUS",
"accessRights": "Manage"
},
{
"type": "sendGrid",
"name": "$return",
"apiKey": "SendGridKey",
"direction": "out",
"from": "ABC#sample.com"
}
],
"disabled": false
}
Then write the run.csx as:
#r "SendGrid"
#r "Newtonsoft.Json"
using System;
using SendGrid.Helpers.Mail;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host;
using Newtonsoft.Json;
public static Mail Run(string telemetryEvent, TraceWriter log)
{
var telemetry = JsonConvert.DeserializeObject<Telemetry>(telemetryEvent);
Mail message = new Mail()
{
Subject = "Write your own subject"
};
var personalization = new Personalization();
personalization.AddBcc(new Email("sample1#test.com"));
personalization.AddTo(new Email("sample2#test.com"));
//add more Bcc,cc and to here as needed
Content content = new Content
{
Type = "text/plain",
Value = $"The temperature value is{temperature.Temperature}."
};
message.AddContent(content);
message.AddPersonalization(personalization);
return message;
}
public class Telemetry
{
public float Temperature { get; set; }
}

Related

Creating an Azure Storage Account in C# using Fluent API with no Allow Blob Public Access

My goal is to create an Azure storage account from C# code using the Fluent API (Microsoft.Azure.Management.Fluent).
My code executes correctly except that my organization has a policy which requires that all storage accounts must be created with "Allow Blob public access" set to Disabled. Is there any way to specify that as an option using the Fluent approach?
TargetStorageAccount = AzureClient.StorageAccounts.Define(storageAccountName)
.WithRegion(Region.AustraliaCentral)
.WithExistingResourceGroup(ResourceGroupName)
.WithSku(StorageAccountSkuType.Standard_LRS)
.WithGeneralPurposeAccountKindV2()
.Create();
You can use ARM template to deploy storage account with "Allow Blob public access" set to Disabled.
For a fast demo, this is my template for the storage account:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storagePrefix": {
"type": "string",
"minLength": 3,
"maxLength": 11
},
"storageSKU": {
"type": "string",
"defaultValue": "Standard_LRS",
"allowedValues": [
"Standard_LRS",
"Standard_GRS",
"Standard_RAGRS",
"Standard_ZRS",
"Premium_LRS",
"Premium_ZRS",
"Standard_GZRS",
"Standard_RAGZRS"
]
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
}
},
"variables": {
"uniqueStorageName": "[concat(parameters('storagePrefix'), '4testonly')]"
},
"resources": [{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-04-01",
"name": "[variables('uniqueStorageName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('storageSKU')]"
},
"kind": "StorageV2",
"properties": {
"supportsHttpsTrafficOnly": true,
"allowBlobPublicAccess": false
}
}
],
"outputs": {
"result": {
"type": "string",
"value": "done"
}
}
}
allowBlobPublicAccess set to false.
This is the code using fluent API to create this deployment:
var storageAccountName = "<your new storage account name>";
var templateJson = File.ReadAllText(#"<path of template file>\storageOnly.json");
azure.Deployments.Define("storageDepolyTest")
.WithExistingResourceGroup("<your resource group name>")
.WithTemplate(templateJson)
.WithParameters("{\"storagePrefix\":{\"value\":\""+ storageAccountName + "\"}}")
.WithMode(DeploymentMode.Incremental)
.Create();
Result:

Microsoft adaptive card

How to send an adaptive card to Microsoft teams channel via SMTP.
I am trying to pass Json through the string in c# via SMTP and send mail directly to the team's channel
string Message = #"{
'type': 'AdaptiveCard',
'body': [
{
'type': 'Container',
'items': [
{
'type': 'TextBlock',
'text': 'Test',
'color': 'Attention',
'wrap': true
}
],
'bleed': true
}
],
'$schema': 'http://adaptivecards.io/schemas/adaptive-card.json',
'version': '1.0'}";
_emailSender.SendEmailAsync("abct#apac.teams.ms", user.Email, "Title", Message);
You should use double quotes " instead single quotes '
You can paste this fixed JSON in Microsoft adaptative designer and check the result.
{
"type": "AdaptiveCard",
"body": [
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Test",
"color": "Attention",
"wrap": true
}
],
"bleed": true
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.0"
}

Properly format a message for slack in bot framework

I'd like to send a message with buttons to the slack channel. I'm using the bot framework (c#). I want to use the "blocks" (attachments are deprecated according to the slack api docs). So I composed a sample message in the slack "Bot Kit Builder":
The json for it looks like this:
[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Which pill do you want to take?"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Red",
"emoji": true
},
"value": "red"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Blue",
"emoji": true
},
"value": "blue"
}
]
}
]
As I understand, I have to provide this content in the ChannelData property of the message I sent to the channel:
if (turnContext.Activity.ChannelId == Channels.Slack)
{
message = turnContext.Activity.CreateReply();
message.ChannelData = ChannelDataBuilder.Create("Which pill do you want to take?", "Red", "Blue");
}
The code of the ChannelDataBuilder looks like this:
public static dynamic Create(string text, params string[] choices)
{
var blocks = new List<Block> { new Section { Text = new Text { TextValue = text } } };
var elements = choices.Select(
c => new Button { Text = new Text { TextValue = c, Type = "plain_text" }, Value = c });
blocks.Add(new Actions { Elements = elements.ToArray() });
return JArray.FromObject(blocks, new JsonSerializer { NullValueHandling = NullValueHandling.Ignore });
}
The resulting json of this method looks like this:
{[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Which pill do you want to take?"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Red"
},
"action_id": "9e8ea9fb9267484a9f02b1837f716f69",
"value": "Red"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Blue"
},
"action_id": "34c3d9509fc04e2ea37ed54a70b78486",
"value": "Blue"
}
]
}
]}
So, basically I wonder how I should generate this array of json object using c#. Currently the array is still surrounded by the curly brackets (the list object), but I guess I have to provide an array of json objects.
I've already tried using the JsonConvert class and setting the ChannelData as string. But then nothing appears in the slack channel.
The channelData property allows you to pass a complete Slack message, but your are missing the required top-level properties.
If you want to include blocks, than those have to be defined under the blocks property.
So your JSON need to look more like this (not include the channelData property):
{
"blocks":
[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Which pill do you want to take?"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Red"
},
"action_id": "9e8ea9fb9267484a9f02b1837f716f69",
"value": "Red"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Blue"
},
"action_id": "34c3d9509fc04e2ea37ed54a70b78486",
"value": "Blue"
}
]
}
]
}
See here for the relevant documentation for the botframework.
And here you can see how a message payload for Slack is defined.
Update
As mentioned by #mdrichardson the botframework currently does not support blocks. (See this issue on their github)
So while syntactically correct this solutions does currently not work.
Until botframework supports blocks I would suggest to use secondary attachments.

Autodesk Forge: Download checklist attachment

When I retrieve a checklist instance, I got the following section related to attachment.
{
"type": "instance_item_attachments",
"id": "5a0a2acf-b02a-4b88-86cc-962c3831bdee",
"attributes": {
"name": "6856ad10-6ab0-11e9-9150-9fda3da0626e.png",
"attachmentType": "OSS",
"mimeType": "image/png",
"uploadStatus": "COMPLETED",
"urns": [
{
"urn": "urn:adsk.wipprod:fs.file:vf.gy4mB910SneymU86Gc4O0A?version=1",
"type": "WIP"
},
{
"urn": "urn:adsk.objects:os.object:wip.dm.prod/ede3de59-1b68-485c-82fe-f1f2af3442fe.png",
"type": "OSS"
},
{
"urn": "urn:adsk.checklists.cs.attachment:58b8afcf-d7cd-49ad-aa10-78c50610761b/5a0a2acf-b02a-4b88-86cc-962c3831bdee",
"type": "CHECKLIST"
}
],
"createdAt": "2019-04-29T18:55:51.334Z",
"updatedAt": "2019-04-29T18:55:54.137Z",
"createdBy": "TAKCJQU6HGXW",
"modifiedBy": "TAKCJQU6HGXW",
"permittedActions": [
"canArchive",
"canEdit"
],
"permittedAttributes": [
"mimeType",
"uploadStatus"
]
},
"links": {
"self": "/containers/58b8afcf-d7cd-49ad-aa10-78c50610761b/instance_item_attachments/5a0a2acf-b02a-4b88-86cc-962c3831bdee"
},
"relationships": {
"container": {
"meta": {
"relation": "primary",
"readOnly": false
},
"links": {
"self": "/containers/58b8afcf-d7cd-49ad-aa10-78c50610761b/instance_item_attachments/5a0a2acf-b02a-4b88-86cc-962c3831bdee/relationships/container",
"related": "/containers/58b8afcf-d7cd-49ad-aa10-78c50610761b/instance_item_attachments/5a0a2acf-b02a-4b88-86cc-962c3831bdee/container"
},
"data": {
"type": "containers",
"id": "58b8afcf-d7cd-49ad-aa10-78c50610761b"
}
},
"item": {
"meta": {
"relation": "primary",
"readOnly": false
},
"links": {
"self": "/containers/58b8afcf-d7cd-49ad-aa10-78c50610761b/instance_item_attachments/5a0a2acf-b02a-4b88-86cc-962c3831bdee/relationships/item",
"related": "/containers/58b8afcf-d7cd-49ad-aa10-78c50610761b/instance_item_attachments/5a0a2acf-b02a-4b88-86cc-962c3831bdee/item"
},
"data": null
}
}
}
Now, I want to download this attachment, the provided URN is: wip.dm.prod/ede3de59-1b68-485c-82fe-f1f2af3442fe.png
If I try to access it using the following link, it says not found
developer.api.autodesk.com/oss/v2/buckets/wip.dm.prod/b30e3ffe-333b-446c-b834-e2f2141096b4.png
However, if I changed the URL a bit (by adding objects), it works fine.
developer.api.autodesk.com/oss/v2/buckets/wip.dm.prod/objects/b30e3ffe-333b-446c-b834-e2f2141096b4.png
Am I doing something wrong here? or this is a bug in the provided urn?
Adding to Adam Nagy reply, you would need to break the URN. From your original question:
urn:adsk.objects:os.object:wip.dm.prod/ede3de59-1b68-485c-82fe-f1f2af3442fe.png
In .NET you can try (using System.Linq):
string bucketKey = urn.Split("/").First().Split(":").Last();
string objectName = urn.Split("/").Last();
Then rebuild as:
string attachemtnUrl = string.Format("{0}/oss/v2/buckets/{1}/objects/{2}", BASE_URL, bucketKey, objectName);
And you'll also need the Authorization header with a valid access token.
The id / urn of an object in OSS (Object Storage Service) contains the bucket name and object name after the "urn:adsk.objects:os.object:" section.
There is a tutorial on downloading a file https://forge.autodesk.com/en/docs/data/v2/tutorials/download-file/
It shows that the reply concerning an item contains both the id and the actual URL of the download link under the storage section:
"storage": {
"data": {
"type": "objects",
"id": "urn:adsk.objects:os.object:wip.dm.prod/977d69b1-43e7-40fa-8ece-6ec4602892f3.rvt"
},
"meta": {
"link": {
"href": "https://developer.api.autodesk.com/oss/v2/buckets/wip.dm.prod/objects/977d69b1-43e7-40fa-8ece-6ec4602892f3.rvt"
}
}
}
There you can see the connection between the id and the URL you can use to download the file

Connecting Azure Function to Azure Storage Blob with HTTP trigger

I came across multiple ways of connecting Azure Storage Blobs to Azure (like this one for example) Functions but all of them require me to use a BlockBlob type parameter in my Run function, therefore replacing the HTTPRequestMessage parameter I need. Is there a way to keep the HTTPRequestMessage parameter and connect to Azure Storage Blob?
Ultimately, I need to get a file reference from the blob to send to another service via Azure Function.
When I try to add more parameters to Run, the function compiles properly, but I am returned a 500 error. When I change the parameters back to two, it works properly. The only difference is the parameter and function.json to which I add a section resulting in the entire file looking like this :
{
"bindings": [
{
"authLevel": "function",
"name": "req",
"type": "httpTrigger",
"direction": "in",
"methods": [
"get",
"post"
]
},
{
"type": "blob",
"name": "myBlobbo",
"path": "mycontainer",
"connection": "value",
"direction": "inout"
},
{
"name": "$return",
"type": "http",
"direction": "out"
}
],
"disabled": false
}
// Alright, now the logs are telling me that I didn't specify a connection string, even though I have a local.settings.json file with this inside:
{
"ConnectionStrings":
{
"xyz": "DefaultEndpointsProtocol=https;AccountName=xxx;AccountKey=yyy;EndpointSuffix=core.windows.net"
}
}
I'm probably going to just connect manually via passing an URI to CloudBlobContainer and using a file stream or %TEMP% to pass the contents, but I still would very much like to know how to get this binding to work.
// I'm using Azure environment to develop the function.
Following example shows how to get blob content with HttpRequest (HttpTrigger, Blob input, Http out):
run.csx
using System.Net;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, string inputBlob, TraceWriter log)
{
log.Info("Blob content: " + inputBlob);
return req.CreateResponse(HttpStatusCode.OK, inputBlob);
}
functions.json
{
"bindings": [
{
"authLevel": "function",
"name": "req",
"type": "httpTrigger",
"direction": "in",
"methods": [
"get",
"post"
]
},
{
"name": "$return",
"type": "http",
"direction": "out"
},
{
"type": "blob",
"name": "inputBlob",
"path": "incontainer/myblob.txt",
"connection": "AzureWebJobsDashboard",
"direction": "in"
}
],
"disabled": false
}
storage:
AzureWebJobsDashboard:

Categories

Resources