sending mails using sendgrid from azure app-service webjob - azure

I have developed a C# console application sending emails to our customers when certain conditions occur. Mails are not sent when application is deployed as azure webjob.
When testing from visual studio in my development environment Mails are sent without any problems as intended, but when the application is deployed and run as an azure app-service webjob no mails are sent. The application is otherwise working as intended also when deployed as azure webjob.
public enum MailType : short { Plain, Html }
private static string MimeType(MailType type)
{ return (type == MailType.Html) ? "text/html" : "text/plain"; }
private static async Task sendMail(string to, string name, string subject, MailType mailType, string content)
{
SendGridMessage msg = new SendGridMessage();
msg.From = new EmailAddress("noreply#foo.org", "Foo Ltd.");
msg.AddTo(to, name);
msg.Subject = subject;
msg.AddContent(MimeType(mailType), content);
SendGridClient client = new SendGridClient(SendGridAPIKey);
await client.SendEmailAsync(msg);
}
public static void SendMail(string to, string name, string subject, MailType mailType, string content)
{
sendMail(to, name, subject, mailType, content).Wait();
}
Answers to all other questions regarding this issue (a lot of developers have met this problem) focus on the code; especially the async implementation, but it seems this is not the issue. Does azure have some restrictions to the communication between the application deployed as webjob and the sendgrid server? (I am using the sendgrid and sendgrid.helpers.mail)

I test with you code and it work well both on local and on Azure. I invoke the SendMail method in a queueTrigger.
public static void ProcessQueueMessage([QueueTrigger("queue1")] string message, TextWriter log)
{
log.WriteLine(message);
SendMail("xxxxx#gmail.com", "xxxxx", "hello world", MailType.Plain, "have a good day.");
}
So you could refer to the following ways to troubleshoot it.
1.Check your SendGridAPIKey. If you stored SendGridAPIKey as an environment variable in the project, you need to go to Configuration>Application Settings and add the key in it.
2.Check your SendGrid Activity.The problem could be getting your request to SendGrid, or it could be that SendGrid is getting your request but isn't fulfilling it. Check the Activity Feed and search for the particular email you are expecting

Related

I can't configure a webhook URI for "Whatsapp Cloud API"

I created an app and configure whatsapp but in configuring webhook I have issues
The error
URL can't be saved. The URL is ok, the endpoint was tested wih postman with no issues.
Server Code
I use Asp.Net Core - WebApi project and create a controller with the method and print in the console a log. I return Ok + challenge if token is equals, otherwise 403 http response.
using Microsoft.AspNetCore.Mvc;
namespace MyApiZ.WebApi.Controllers
{
[ApiController]
[Route("")]
public class MessageController : Controller
{
const string VerfifyToken = "1234";
[HttpGet("webhook")]
public ActionResult<string> SetupWebHook([FromQuery(Name = "hub_mode")] string hubMode,
[FromQuery(Name = "hub_challenge")] int hubChallenge,
[FromQuery(Name = "hub_verify_token")] string hubVerifyToken)
{
Console.WriteLine("█ WebHook with get executed. ");
Console.WriteLine($"█ Parameters: hub_mode={hubMode} hub_challenge={hubChallenge} hub_verify_token={hubVerifyToken}");
if (!hubVerifyToken.Equals(VerfifyToken))
{
return Forbid("VerifyToken doesn't match");
}
return Ok(hubChallenge);
}
[HttpPost("webhook")]
public ActionResult ReceiveNotification([FromBody] string data)
{
Console.WriteLine("█ WebHook with Post executed. ");
Console.WriteLine(data);
return Ok();
}
public IActionResult Index()
{
return View();
}
}
}
Test with Postman
No issues in request from Postman
My app is hosted in Azure App Service. I checked the app log in Azure Portal.
When the request is executed in Postman, the messages are printed. But when click on facebook in the "Verify and save button" the error is present, the message aren't print in the log, the endpoint is never called.
I have written a brief article on WhatsApp Cloud API like how to send and receive WhatsApp messages and also set up a never expiry access token. Please have a look enter link description here
You will get your answer from my article. BTW when you click the verify & save button then you will get the response from WhatsApp to your callback URL. You just need to respond back the hub_challenge key(sent from whatsapp) respond back to whatsapp with 200 status code then your webhook should automatically verify
Simple change
from
public ActionResult<string> SetupWebHook([FromQuery(Name = "hub_mode")] string hubMode,
[FromQuery(Name = "hub_challenge")] int hubChallenge,
[FromQuery(Name = "hub_verify_token")] string hubVerifyToken)
to
public ActionResult<string> SetupWebHook([FromQuery(Name = "hub.mode")] string hubMode,
[FromQuery(Name = "hub.challenge")] int hubChallenge,
[FromQuery(Name = "hub.verify_token")] string hubVerifyToken)
Once you click the Verify and Save button then you will get the API request from WhatsApp to your server webhook callback URL.
Now, your webhook needs to respond back > hub_challenge.

Sending email via Azure Functions from my company Azure subscription using company email addresses for sender and receivers

I want to send email via Azure Functions hosted on my company Azure subscription using company email addresses for both sender and recipient.
Is it possible to use SMTP server like below:
var fromAddress = new MailAddress("from#myfirm", "From Name");
var toAddress = new MailAddress("to#myfirm", "To Name");
const string fromPassword = "from password";
string subject = "Subject";
string body = "Body";
var smtp = new SmtpClient
{
Host = "my company's smtp server",
Port = 587,
EnableSsl = true,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Credentials = new NetworkCredential(fromAddress.Address, fromPassword)
};
using (var message = new MailMessage(fromAddress, toAddress)
{
Subject = subject,
Body = body
})
{
smtp.Send(message);
}
Refs
https://learn.microsoft.com/en-in/azure/sendgrid-dotnet-how-to-send-email
How do i send email from Azure function app
Update:
What if a dedicated IP address is used for Azure app that sends out email using SMTP? Would this eliminates the issue? If not, what other potential issues?
Update:
Starting on November 15, 2017, outbound email messages that are sent
directly to external domains (such as outlook.com and gmail.com) from
a virtual machine (VM) are made available only to certain subscription
types in Microsoft Azure. Outbound SMTP connections that use TCP port
25 were blocked. (Port 25 is primarily used for unauthenticated email
delivery.)
This change in behavior applies only to new subscriptions and new
deployments since November 15, 2017.
This is the doc:
https://learn.microsoft.com/en-us/azure/virtual-network/troubleshoot-outbound-smtp-connectivity
Azure recommend to use third party SMTP relay like SendGrid to send email.
Original Answer:
I am not sure the language you are using now, so I assume you are using C#.
Below is how to use SendGrid in azure function:
For example, if you want to send email from email A to email B.
First of all, install package Microsoft.Azure.WebJobs.Extensions.SendGrid.
go to the sendgrid website, create a sender and verify the email A:
After that, email A is ready to send emails by sendgrid.
Now we need to generate a SendGrid API key and copy and store the API key:(This will be filled in the Values section of local.settings.json as an environment variable to read.)
Then, you can use it to send emails
Use SendGrid Binding:
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using SendGrid.Helpers.Mail;
namespace FunctionApp40
{
public static class Function1
{
[FunctionName("Function1")]
public static void Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
[SendGrid(ApiKey = "CustomSendGridKeyAppSettingName")] out SendGridMessage message,
ILogger log)
{
message = new SendGridMessage();
message.AddContent("text/plain","This is a test.");
message.SetFrom("emailA#emailA.com");
message.AddTo("emailB#emailB.com");
message.SetSubject("This is send from Function app.");
log.LogInformation("This is a test.");
}
}
}
local.settings.json:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"CustomSendGridKeyAppSettingName": "SG.XQ9uKeO3QCmNH7hA8qP8eA.xxxxxx"
}
}
Works fine on my side:
By the way, this can only guarantee successful delivery, because some mailboxes refuse to accept mail sent through sendgrid. In this case, you will still get a 200 response, but the recipient will not receive the mail.(In this case, the recipient needs to go to the mailbox settings to lift the relevant restrictions.)

How to send Azure SignalR messages to clients on all instances of a multi-instance Azure Application

We are evaluating how to send messages to connected clients via SignalR. Our application is published in Azure, and has multiple instances. We are able to successfully pass messages to clients connected to the same instance, but not other instances.
We initially were looking at ServiceBus, but we (perhaps mistakenly) found out that AzureSignalR should basically be a service bus that handles all of the backend stuff for us.
We set up signalR in Startup.cs such as:
public void ConfigureServices(IServiceCollection services)
{
var signalRConnString = Configuration.GetConnectionString("AxiomSignalRPrimaryEndPoint");
services.AddSignalR()
.AddAzureSignalR(signalRConnString)
.AddJsonProtocol(options =>
{
options.PayloadSerializerSettings.ContractResolver = new DefaultContractResolver();
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAzureSignalR(routes =>
{
routes.MapHub<CallRegistrationHub>("/callRegistrationHub");
routes.MapHub<CaseHeaderHub>("/caseHeaderHub");
routes.MapHub<EmployeesHub>("/employeesHub");
});
}
Issue
We have to store some objects that should probably be on the service bus, and not stored in an individual instance; However, I am unsure of how to tell the hub that the objects should be on the bus and not internal to that specific instance of the hub, as below:
public class EmployeesHub : Hub
{
private static volatile List<Tuple<string, string, string,string, int>> UpdateList = new List<Tuple<string, string, string,string,int>>();
private static volatile List<Tuple<string, int>> ConnectedClients = new List<Tuple<string, int>>();
}
We have functions that need to send messages to all connected clients that are looking at the current record regardless of in what instance they reside:
public async void LockField(string fieldName, string value, string userName, int IdRec)
{
var clients = ConnectedClients.Where(x => x.Item1 != Context.ConnectionId && x.Item2 == IdRec).Select(x => x.Item1).Distinct().ToList();
clients.ForEach(async x =>
{
await Clients.Client(x).SendAsync("LockField", fieldName, value, userName, true);
});
if (!UpdateList.Any(x=> x.Item1 == Context.ConnectionId && x.Item3 == fieldName && x.Item5 == IdRec))
{
UpdateList.Add(new Tuple<string, string, string,string,int>(Context.ConnectionId,userName, fieldName, value, IdRec));
}
}
This is not working for different instances (which makes sense, because each instance will have its own objects.. However, we were hoping that by using AzureSignalR instead of SignalR (AzureSignalR conn string has an endpoint to the Azure service) that it would handle the service bus functionality for us. We are not sure what steps to take to get this functioning correctly.
Thanks.
The reason for this issue is that I was preemptively attempting to limit message traffic. I was attempting to only send messages to clients that were looking at the same record. However, because my objects were instance-specific, it would only grab the connection IDs from the current instance's object.
Further testing (using ARR affinity) proves that on a Clients.All() call, all clients, including those in different instances, receive the message.
So, our AzureSignalR setup appears to be correct.
Current POC Solution - currently testing
-When a client registers, we will broadcast to all connected clients "What field do you have locked for this Id?"
-If client is on a different Id, it will ignore the message.
-If client does not have any fields locked, it will ignore the message.
-If client has a field locked, it will respond to the message with required info.
-AzureSignalR will then rebroadcast the data required to perform a lock.
This increases message count, but not significantly. But it will resolve the multiple instances holding different connected ClientIds issue.
Just a thought, but have you tried using SignalR Groups? https://learn.microsoft.com/en-us/aspnet/core/signalr/groups?view=aspnetcore-2.2#groups-in-signalr
You could try creating a group for each combination of IdRec and fieldName and then just broadcast messages to the group. This is the gist of how I think your LockField function might look:
public async void LockField(string fieldName, string value, string userName, int IdRec)
{
string groupName = GetGroupName(IdRec, fieldName);
await Clients.Group(groupName).SendAsync("LockField", fieldName, value, userName, true);
await this.Groups.AddToGroupAsync(Context.ConnectionId, groupName);
}
You could implement the GetGroupName method however you please, so long as it produces unique strings. A simple solution might be something like
public string GetGroupName(int IdRec, string fieldName)
{
return $"{IdRec} - {fieldName}";
}

How to view response of Luis.ai in azure web bot app

I am working on a web bot app and I link it with luis.ai
I want to view the response of luis like which intent is been called and what was the entity called. I am using bot emulator version 4, but you can't find any type of info related to your intent or entities in it.
Is there any way we can see the json response of luis.ai ?
So that I can start building my bot further.
I am asking this because look How am I gonna know what's the format of luis response, how to get data from it as long as I don't know In which format i an receiving the response.
any details tutorial please?
There two ways to see luis response.
you can go to luis.ai and then copy the url of pulishment(in pulish section). And paste it in the navegator. You will get url like this: 'https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/xxxx?subscription-key=xxxxxxxxxx&verbose=true&timezoneOffset=0&q=your sentence', and setup q= your sentence.
Another way, you can log all luis conversation in you code. If you use c sharp bot builder, you can use this luis class.
[Serializable]
public class LogedLuisService : ILuisService
{
private ILuisService service;
private string moduleType;
public LogedLuisService(ILuisService service)
{
this.service = service;
}
public Uri BuildUri(LuisRequest luisRequest)
{
return service.BuildUri(luisRequest);
}
public LuisRequest ModifyRequest(LuisRequest request)
{
return service.ModifyRequest(request);
}
public Task<LuisResult> QueryAsync(Uri uri, CancellationToken token)
{
return service
.QueryAsync(uri, token)
.ContinueWith(
task => {
Trace.WriteLine("Luis: " + " : " + JsonConvert.SerializeObject(task.Result));
return task.Result;
});
}}

Windows Azure worker role and SendGrid "Bad Key Path!" Error

I'm trying to use SendGrid to send an email from an Azure worker role every time there are certain exceptions, but I can't get the email to send. I am using SendGridMail version 6.1.0.0 and SendGrid.SmtpApi version 1.3.1.0 which I installed via nuget and .Net 4.5. I am currently debugging locally with plans to deploy to Azure if i can get the emails to successfully send.
SendGridMessage myMessage = new SendGridMessage();
List<String> recipients = new List<String> { #"John Doe <johnd#outlook.com>", #"Peter Howe <perterhowe#gmail.com>" };
myMessage.AddTo(recipients);
myMessage.From = new MailAddress("myemail#test.com");
myMessage.Subject = "Error in Update";
myMessage.Text = "TESTING 123";
string username = XXXXXX;
string password = XXXXXXX;
// Create credentials, specifying your user name and password.
var credentials = new NetworkCredential(username, password);
// Create an Web transport for sending email.
var transportWeb = new Web(credentials);
// Send the email.
await transportWeb.DeliverAsync(myMessage);
As far as I can see I'm not getting any errors except when I debug and look at myMessage the Header has an error.
When I tried initializing a new empty header (var header = new Header();) I noticed there were still errors on that
To = 'header.To' threw an exception of type 'System.ArgumentException' Message = "Bad key path!"
Does anyone know what this means? Or if this could be causing the emails not to send?
The answer to your other question actually uses SendGrid:
Alerts for exceptions in an Azure worker role
There are three globalvariables:
public const string SmtpServerHost = "smtp.sendgrid.net";
public const string SmtpServerUserName = "[useridfromsendgrid#azure.com]";
public const string SmtpServerPassword = "[password from sendgrid]";
You actually do not need to use the SDK, just setup the account in Azure portal, and save your creds in your project.
You can send emails locally, but if you are on a work network, the firewall may block the emails from being sent. The code I posted I placed in an email service in my namespace.
It has be deployed to Azure to work. It won't work locally.

Resources