Mail vom AWS IoT Button senden in C#

Ich habe ja bereits in einem meiner letzten Beiträge gezeigt, wie ihr euch euren AWS IoT Button einrichten könnt. Ebenso habe ich euch beispielhaft gezeigt, wie ihr eine vorgefertigte NodeJS Lambda-Funktion zum Senden einer eMail-Nachricht als Standardfunktion festlegen könnt, welche dann beim Drücken des Buttons ausgeführt wird. In dem heutigen Beitrag möchte ich euch nun zeigen, wie ihr diese Funktion selbst schreiben könnt und zwar in C# mit der Hilfe von Visual Studio.

Wir öffnen Visual Studio und sollte noch einmal sicherstellen, dass wir das AWS Toolkit für Visual Studio installiert haben. Ich habe die entsprechende Erweiterung für VS bereits hier vorgestellt. Anschließend erstellen wir ein neues Projekt und wählen als Grundlage AWS Lambda Project (.NET Core) aus der Gruppe AWS Lambda aus.

Es gibt bereits einige Vorlagen, auch Blueprints genannt, aber wir wollen alles von Hand erstellen und wählen daher im Wizard den Eintrag Empty Function aus und bestätigen mit einem Klick auf den Finish-Button.

Nun erstellen wir uns einen Ordner Models und legen hier die Datenstruktur für das JSON als Klassen ab, welches uns der Button beim Drücken an unsere Funktion senden wird. Hierzu beginnen wir mit einm Enum, welches uns angibt, wie der Nutzer geklickt hat, denn es gibt drei Variationen. So kann der Nutzer einmal kurz, zweimal kurz oder einmal lang den Button drücken. Daher erhalten wir folgende Struktur:

public enum ClickType
{
   Unknown,
   Single,
   Double,
   Long
}

Neben dem ClickType erhalten wir auch noch die Seriennummer des Buttons und die aktuelle Batterie-Spannung. Daher erhalten wir das folgende Datenmodell:

public class IoTButtonEvent
{
   [JsonProperty("serialNumber")]
   public string SerialNumber { get; set; }

   [JsonProperty("batteryVoltage")]
   public string BatteryVoltage { get; set; }

   [JsonProperty("clickType")]
   [JsonConverter(typeof(StringEnumConverter))]
   public ClickType ClickType { get; set; }
}

Da eine Email-Adresse zunächst bestätigt werden muss, bevor auf dieser Nachrichten empfangen werden können, erstellen wir noch die Klasse EmailStatus, welche für uns intern verwendet wird um den aktuellen Status zu speichern.

public class EmailStatus
{
   public bool IsVerified { get; }
   public string ErrorMessage { get; }


   public EmailStatus(bool isVerified, string errorMessage)
   {
      IsVerified = isVerified;
      ErrorMessage = errorMessage;
   }
}

Damit sind unsere drei Modell-Klassen bereits vollständig und wir können uns um die eigentliche Logik kümmern. Oberhalb der Methode FunctionHandler fügen wir ein paar private Variablen hinzu, welche wir im Verlauf benötigen:

private readonly AmazonSimpleEmailServiceClient _amazonSimpleEmailServiceClient 
  = new AmazonSimpleEmailServiceClient();

private const string RecipientEnvironmentKey 
  = "Recipient";
private const string VerificationEmailWarning 
  = "A verification email sent to the provided mail address. You first need to verify it.";

Damit uns die Klasse AmazonSimpleEmailServiceClient zur Verfügung stellt, müssen wir das NuGet Package AWSSDK.SimpleEmail installieren und den passenden Namespace hinzufügen. Nun ändern wir direkt die Signatur der Methode FunctionHandler, denn wir wollen nun Zugriff auf unser Modell erhalten:

public async Task FunctionHandler(IoTButtonEvent buttonEvent, ILambdaContext context)
{
}

Nun fügen wir eine Methode SendVerificationEmailAsync hinzu. Wie der Name schon vermuten lässt, wird diese Methode bei Bedarf die Verifikations-Mail an die hinterlegte Email-Adresse senden. Hierzu stellt uns der AmazonSimpleEmailServiceClient eine passende Methode zur Verfügung. Anschließend erstellen wir noch unser EmailStatus-Objekt und geben dies zurück.

private async Task<EmailStatus> SendVerificationEmailAsync(string email)
{
   var request = new VerifyEmailIdentityRequest { EmailAddress = email };
   await _amazonSimpleEmailServiceClient.VerifyEmailIdentityAsync(request);

   return new EmailStatus(false, VerificationEmailWarning);
}

Im nächsten Schritt erstellen wir die Methode CheckEmailStatusAsync, welche für uns überprüft, ob die Email-Adresse bereits verifiziert wurde. Sollte dies nicht der Fall sein, so wird automatisch die Verifikations-Mail gesendet.

private async Task<EmailStatus> CheckEmailStatusAsync(string email)
{
   var request = new GetIdentityVerificationAttributesRequest
   {
      Identities = new List<string> { email } 
   };
   
   var response = await _amazonSimpleEmailServiceClient
     .GetIdentityVerificationAttributesAsync(request);
   
   var attributes = response.VerificationAttributes;
   
   if (attributes.TryGetValue(email, 
     out IdentityVerificationAttributes verificationAttributes))
   {
      if (verificationAttributes.VerificationStatus 
	    == VerificationStatus.Success)
	  {
        return new EmailStatus(true, string.Empty);
      }
   }

   return await SendVerificationEmailAsync(email);
}

Nun folgt die eigentliche Logik innerhalb der Methode FunctionHandler. Hier holen wir uns über die Umgebungsvaribale der Funktion die Email-Adresse und überprüfen den Email-Status. Sollte die Email-Adresse bereits bestätigt sind, so erzeugen wir uns ein entsprechendes Objekt mit allen Informationen, welche wir senden wollen und lassen den AmazonSimpleEmailServiceClient die Nachricht schicken.

public async Task FunctionHandler(IoTButtonEvent buttonEvent, ILambdaContext context)
{
   var email = Environment.GetEnvironmentVariable(RecipientEnvironmentKey);
   var payload = JsonConvert.SerializeObject(buttonEvent);

   var emailStatus = await CheckEmailStatusAsync(email);

   if (!emailStatus.IsVerified)
   {
     Console.WriteLine(emailStatus.ErrorMessage);
     return;
   }

   var subject = $"{buttonEvent.SerialNumber}";
   var body = $"{buttonEvent.SerialNumber}: {payload}.";

   var request = new SendEmailRequest
   {
      Source = email,
      Destination = new Destination(new List<string> { email }),
      Message = new Message(new Content(subject), new Body(new Content(body)))
   };

   await _amazonSimpleEmailServiceClient.SendEmailAsync(request);
}

Bevor wir jetzt die Funktion zu AWS hochladen können, müssen wir noch eine passende Rolle in AWS definieren, damit wir die entsprechenden Berechtigungen haben, um eine Email-Nachricht zu senden. Dazu öffnen wir einen Browser, navigieren auf https://aws.amazon.com und loggen und mit unseren Credentials ein.

In der Suchleiste geben wir nun IAM ein, um in den Bereich zur Nutzerverwaltung zu gelangen.

Hier erhalten wir eine Übersicht, über Nutzer und auch Rollen. Wir klicken auf den Link Roles in der Mitte des Bildschirms oder am linken Rand auf den Eintrag Roles.

Da wir eine neue Rolle erstellen wollen, klicken wir auf den Button Create role, um diesen Prozess zu starten.

Im nächsten Schritt suchen wir Lambda, da dies unser Basis-Profil ist.

Wir fügen nun bereits vorhanden Berechtigungen hinzu. Hierfür filtern wir die verfügbaren Richtlinien nach SES und wählen den Eintrag AmazonSESFullAccess aus.

Den nächsten Schritt können wir mit Next: Review überspringen, da wir keine Tags in diesem Fall benötigen.

Nun können wir noch einen Rollen-Namen vergeben. Ich habe mir hier für lambda_sesfullaccess_role entschieden, denn so kann man direkt die Einstellungen der Role auf Grund des Namens wissen. Wir schließen den Prozess über den Button Create role ab.

Unsere Rolle wurde erfolgreich hinzugefügt und wird und nun auch in der Übersicht angezeigt.

Wir können nun wieder in Visual Studio zurückgehen und unsere Funktion nach AWS hochladen. Hierbei können wir jetzt auch unsere neue Rolle entsprechend angeben. Wenn wir dann wieder in die AWS-Konsole wechseln, sehen wir im Bereich Lambda unsere erstellte Funktion.

Wenn wir nun die Funktion öffnen müssen wir in den Umgebungsvariablen noch einen neuen Eintrag mit dem Schlüssel Recipient eintragen und als Wert eine Email-Adresse für den Empfänger der Nachrichten.

Nun können wir bequem über die App die Funktion dem Button zuweisen und bei jedem Drücken des Buttons wird nun eine Nachricht gesendet.

Dieser Beitrag sollte zeigen, wie man eine Email-Nachricht senden kann, wenn jemand den AWS IoT Button drückt und gleichzeitig eine Anleitung sein, wie man selbst Funktionen schreiben kann, welche beim Drücken des Buttons ausgeführt werden. Dieses Beispiel findet man auch auf bei mir auf GitHub. Ich wünsche nun viel Spaß beim Entwickeln eurer eigenen Funktionen.

Alexa-Skill über VSTS auf AWS veröffentlichen Buch-Tipp: Hilfe, ich habe meine Privatsphäre aufgegeben! von Barbara Wimmer Entwicklung eines Alexa-Skills #2