<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: James Naylor</title>
    <description>The latest articles on DEV Community by James Naylor (@euronay).</description>
    <link>https://dev.to/euronay</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F179099%2F0d359486-0bd7-4e4d-948b-f126e4e30865.jpg</url>
      <title>DEV Community: James Naylor</title>
      <link>https://dev.to/euronay</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/euronay"/>
    <language>en</language>
    <item>
      <title>Let's build an Alexa Skill with .NET and pizza 🍕</title>
      <dc:creator>James Naylor</dc:creator>
      <pubDate>Wed, 29 Jan 2020 12:17:30 +0000</pubDate>
      <link>https://dev.to/euronay/let-s-build-an-alexa-skill-with-net-and-pizza-46le</link>
      <guid>https://dev.to/euronay/let-s-build-an-alexa-skill-with-net-and-pizza-46le</guid>
      <description>&lt;p&gt;&lt;em&gt;Alexa&lt;/em&gt; is the virtual assistant bundled with Amazon's Echo range of devices. Like &lt;em&gt;Google Assistant&lt;/em&gt; and Apple's &lt;em&gt;Siri&lt;/em&gt;, Alexa offers a &lt;strong&gt;Voice User Interface&lt;/strong&gt; (&lt;strong&gt;VUI&lt;/strong&gt;) to Amazon's services as well as a huge number of 3rd party services through &lt;strong&gt;Skills&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;I've written a small skill before - my Magic the Gathering card finder &lt;a href="https://www.amazon.co.uk/James-Naylor-MTG-Wizard/dp/B07W225C35/"&gt;MTG Wizard&lt;/a&gt; - and I learnt a whole heap of stuff along the way. However this was written in javascript which is not my strongest skill. Now I've found that you can write an Alexa Skill using .NET - much better!&lt;/p&gt;

&lt;p&gt;Today I want to show you how to create a simple Alexa Skill with a .NET function hosted on Azure. We will do this by creating a simple skill to allow a user to create a pizza order and send it to our imaginary pizzeria, &lt;em&gt;Pizzavendolo!&lt;/em&gt; 🍕&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a new Skill
&lt;/h2&gt;

&lt;p&gt;First step is to head on over to the &lt;a href="https://developer.amazon.com/alexa/console/ask"&gt;Alexa Developer Console&lt;/a&gt; and (assuming you have signed up) hit "Create Skill". Here you need to pick a name for your skill - in our case &lt;em&gt;Pizzavendolo!&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;We are building a simple Model so we use the "Custom" model type and we are going to be hosting our backend on Azure, so pick "Provision your own" in the backend resources section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P81jgSHd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/o76dhbbkuv3cgsxm9zyz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P81jgSHd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/o76dhbbkuv3cgsxm9zyz.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have our project set up and we can configure everything we need. Along the top of the screen there are several options. Right now we are interested in the "Build" tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ks532ygI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/w4umngm173r7v749z56q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ks532ygI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/w4umngm173r7v749z56q.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the Interaction Model
&lt;/h2&gt;

&lt;p&gt;The interaction model defines all of the rules that will parse what a user has said to their Alexa device and where and what is passed to our function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Invocation
&lt;/h3&gt;

&lt;p&gt;Firstly we are going to set up the it's &lt;em&gt;Invocation&lt;/em&gt; name - this what a user would say to start our application. Our invocation, "pizza vendelo", means that a user can say "Alexa, ask Pizza Vendele to order a large pizza" and our app will start to process the request. Yum 😋!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NHn_xFPN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1gwmkhhy6ce0tzo18k96.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NHn_xFPN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1gwmkhhy6ce0tzo18k96.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Intents and Slots
&lt;/h3&gt;

&lt;p&gt;A skill is then made up of a series of &lt;em&gt;Intents&lt;/em&gt;, which is what the user wants to achieve in that moment. The intent is determined from the user's &lt;em&gt;utterances&lt;/em&gt; - what they have said. You will see a number of built-in intents that handle general actions the user might want to take in your skill, such as exiting or asking for help.&lt;/p&gt;

&lt;p&gt;We are going to create a new intent called &lt;em&gt;OrderPizzaIntent&lt;/em&gt; that will allow the user to say something like "order a large pizza".&lt;/p&gt;

&lt;p&gt;Click "Add" next to the "Intents" heading, then choose "Create custom intent" and enter the name - &lt;em&gt;OrderPizzaIntent&lt;/em&gt;. We can now see the configuration screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6lVlXP1t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/23vbkmcuauvzj61j8zi2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6lVlXP1t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/23vbkmcuauvzj61j8zi2.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each &lt;em&gt;intent&lt;/em&gt; is made up of a number of &lt;em&gt;utterances&lt;/em&gt;. These are example phrases that the user might say that will be handled by this &lt;em&gt;intent&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Each &lt;em&gt;utterance&lt;/em&gt; consists of a &lt;em&gt;carrier phrase&lt;/em&gt; and a number of &lt;em&gt;slots&lt;/em&gt;. We want the user to be able to choose and amount, the toppings and what size of pizza to order.&lt;/p&gt;

&lt;p&gt;The user might say something like "Order &lt;em&gt;2&lt;/em&gt; &lt;em&gt;medium&lt;/em&gt; &lt;em&gt;margherita&lt;/em&gt; pizzas". Here "Order ... pizzas" is the &lt;em&gt;carrier phrase&lt;/em&gt; and there are three &lt;em&gt;slots&lt;/em&gt; - the quantity, the size and the toppings.&lt;/p&gt;

&lt;p&gt;We are going to create some &lt;em&gt;slots&lt;/em&gt; which will define what we expect using a value from a list. To define the list, we to create a new &lt;em&gt;slot type&lt;/em&gt; by clicking on "Add" next to "Slot Types" on the left. Choose "Create custom slot type" and call it "Toppings". We capture each different option we expect the user to say and also synonyms for each option.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OCVK9jD7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/28f0v84uzr8t1fdpq6wa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OCVK9jD7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/28f0v84uzr8t1fdpq6wa.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next add another &lt;em&gt;slot type&lt;/em&gt; called "Size" with the option for "Small", "Medium" and "Large".&lt;/p&gt;

&lt;p&gt;We can play around with the slot synonyms and utterances once the skill is working to fine tune the capturing of the users intent&lt;/p&gt;

&lt;p&gt;Next go back to our OrderPizzaIntent and create a new &lt;em&gt;intent slot&lt;/em&gt; called "toppings" and choose our "Toppings" slot type. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4zcS8PA4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/k5mzxgsih99zld122vuo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4zcS8PA4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/k5mzxgsih99zld122vuo.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We want to ensure that the user provides a value for the topping they would like. If they do not, we will aske them. Click on "Edit Dialog" next to the slot.&lt;/p&gt;

&lt;p&gt;Form this page we want to enable "slot filling" and enter a prompt that Alexa will say if a topping is not mention - &lt;em&gt;What would you like on your pizza?&lt;/em&gt; - and we enter the &lt;em&gt;utterances&lt;/em&gt; we would expect the user to say in response to the prompt. We'll cover &lt;em&gt;utterances&lt;/em&gt; a little more shortly, but here we will expect them to just say a topping.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uEMbYtva--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7au3dkqc55nofl3dot6l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uEMbYtva--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7au3dkqc55nofl3dot6l.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now go back to the intent page and create another &lt;em&gt;slot&lt;/em&gt; for "Size" and "Quantity". We can use one of the built-in slot types for the quantity - AMAZON.NUMBER - which will detect a number in the user's request.&lt;/p&gt;

&lt;p&gt;Next start filling in the sample &lt;em&gt;utterances&lt;/em&gt;. The slots can be inserting into the utterance inside curly braces - so an example might be "Order {quantity} {size} {toppings} pizza". Make sure you enter a range of variations to ensure it is picked up. This is where some experimentation comes in!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7mVfCgeq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/t1a964f0ndajanzz9ata.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7mVfCgeq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/t1a964f0ndajanzz9ata.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally click "Save Model" and "Build Model" to compile the model. We can then use the "Evaluate Model" button to test some phrases and see that Alexa evaluates the correct intent and populates the slot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T2jjaC-r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/q600hehh0udla64p7svy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T2jjaC-r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/q600hehh0udla64p7svy.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Excellent - we are now well on our way to some delicious pizza! 😃&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Function
&lt;/h2&gt;

&lt;p&gt;Now we have our model set up we need to create a backend for it. You can opt to use the Alexa-hosted function right at the beginning of setting up the skill. This will allow you to edit the code directly in the Alexa developer console from the "Code" tab. But we are going to write our backend as an &lt;a href="https://azure.microsoft.com/nl-nl/blog/introducing-azure-functions/"&gt;Azure Function&lt;/a&gt; using .NET!&lt;/p&gt;

&lt;p&gt;The first thing you are going to need is the excellent &lt;a href="https://marketplace.visualstudio.com/items?itemName=MarcoMinerva.AlexaSkillProjectTemplate"&gt;Alexa Skill Project Template&lt;/a&gt;. Once you have installed that, let's fire up Visual Studio and get to work.&lt;/p&gt;

&lt;p&gt;Create a new project using the "Alexa Skill" template&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YhExaXm5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xxqcgueo0465pntzzycd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YhExaXm5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xxqcgueo0465pntzzycd.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will create a new project with everything needed by the skill's backend. It uses the &lt;a href="https://github.com/timheuer/alexa-skills-dotnet"&gt;Alexa Skills SDK for .NET&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bsqxokuF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2vrrbtxkftaliosf5gtd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bsqxokuF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2vrrbtxkftaliosf5gtd.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The implemetation is in &lt;code&gt;Skill.cs&lt;/code&gt; and already includes support for handling the built-in intents as well as languages and validating the requests.&lt;/p&gt;

&lt;p&gt;This function takes the json request provided by Alexa, deserialises it into a &lt;code&gt;SkillRequest&lt;/code&gt; object and then allows us to parse it's contents to determine what to say back in response. We will examine the request sent to us once we wire up our function.&lt;/p&gt;

&lt;p&gt;First we will change the welcome message. When the skill is first instatiated by Alexa, a &lt;code&gt;LaunchRequest&lt;/code&gt; is sent to our function. This is handled by this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;LaunchRequest&lt;/span&gt; &lt;span class="n"&gt;launchRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Session started"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;welcomeMessage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LanguageKeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Welcome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;welcomeRepromptMessage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LanguageKeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WelcomeReprompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ResponseBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;welcomeMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RepromptBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;welcomeRepromptMessage&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You will note that this returns a message as well as a &lt;em&gt;reprompt&lt;/em&gt; message. When you get Alexa to ask a question, you can set a reprompt to be said if she does not get a response. This is using the template's prebuilt localisation, so we can change the default strings at the bottom of the class. So we will simply change the line in the &lt;code&gt;SetupLanguageResources&lt;/code&gt; method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;LanguageKeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Welcome&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Welcome to Pizza Vendolo! What would you like to order?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next we see the handler for further requests. There are many different request types that can be sent to the function depending on your model, such as requesting to start playing audio, or when someone touches a control on the screen. For now we are just going to stick to the basics and handle &lt;code&gt;IntentRequest&lt;/code&gt;s.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;IntentRequest&lt;/span&gt; &lt;span class="n"&gt;intentRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After this line you will see the handlers for the built-in &lt;em&gt;intents&lt;/em&gt; - cancel, help, stop etc. Then we can add our code to deal with our new &lt;em&gt;OrderPizzaIntent&lt;/em&gt; we set up in the model.&lt;/p&gt;

&lt;p&gt;So after the following comment we will add a handler for this which will perform our action and report back. Our &lt;em&gt;toppings&lt;/em&gt;, &lt;em&gt;size&lt;/em&gt; and &lt;em&gt;quantity&lt;/em&gt; slot values can be retrieved from the intent.&lt;/p&gt;

&lt;p&gt;Bear in mind as this is a very simple example!&lt;/p&gt;

&lt;p&gt;For now we are going to simply parse the request and say it back to the user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Processes request according to intentRequest.Intent.Name...&lt;/span&gt;

&lt;span class="c1"&gt;// Handle OrderPizzaIntent&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intentRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"OrderPizzaIntent"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;intentRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Slots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"toppings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;toppingsSlot&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;intentRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Slots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;quantitySlot&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;intentRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Slots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sizeSlot&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quantitySlot&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Convert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToInt32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quantitySlot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;intentRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Slots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"die_type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dieTypeSlot&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;PlainTextOutputSpeech&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"You ordered &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sizeSlot&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;toppingsSlot&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; pizzas!"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ResponseBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Tell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After this, we publish our function to Azure. If you are unsure how to do this, follow the steps on &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-develop-vs#publish-to-azure"&gt;Microsoft Docs here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once we have published our function, we simply need the url to our endpoint which in our case is &lt;a href="https://pizzavendolo.azurewebsites.net/api/PizzaVendolo"&gt;https://pizzavendolo.azurewebsites.net/api/PizzaVendolo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;## Connecting and Testing&lt;/p&gt;

&lt;p&gt;Now it's time to head back to the &lt;a href="https://developer.amazon.com/alexa/console/"&gt;Alexa Developer Console&lt;/a&gt; and select "Endpoint" from the menu. We enter our URL into the box and select the "My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority" option&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AmtfsBsq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ot0ctbg508yjgy9jq6tv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AmtfsBsq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ot0ctbg508yjgy9jq6tv.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To test our Skill, we can use the "Test" tab from the top bar. This is the emulator that allows us to speak or write commands to Alexa and see the request. You can also test your skill using a real device as long as it is registered to the account you use for developing!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jjEeX9Nk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/mlexksgs9s6bbmrme57k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jjEeX9Nk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/mlexksgs9s6bbmrme57k.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Success! We have set up our skill and connected it to a function 🍕🎉&lt;/p&gt;

&lt;p&gt;Soon I will write a part 2 where we will build out our skill a little more. Hopefully see you there!&lt;/p&gt;

&lt;p&gt;In the meantime, get in touch with me on  Twitter: &lt;a href="https://twitter.com/euronay"&gt;@euronay&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>azure</category>
      <category>alexa</category>
    </item>
    <item>
      <title>Automatically deploy your .NET Core app using Github Actions</title>
      <dc:creator>James Naylor</dc:creator>
      <pubDate>Wed, 08 Jan 2020 13:04:12 +0000</pubDate>
      <link>https://dev.to/euronay/automatically-deploy-your-net-core-app-using-github-actions-45a</link>
      <guid>https://dev.to/euronay/automatically-deploy-your-net-core-app-using-github-actions-45a</guid>
      <description>&lt;p&gt;I recently discovered &lt;a href="https://github.com/features/actions"&gt;Github Actions&lt;/a&gt; as an amazingly simple way to set up continuous integration for my .NET Core web apps. &lt;/p&gt;

&lt;p&gt;A long time ago, &lt;a href="https://jamesnaylor.dev/Posts/Read?id=dotnet-app-on-vps"&gt;I wrote about how I host my webapps on a small Linux VPS&lt;/a&gt;. To deploy a new version I have to build my web app, copy the files over onto my server and then restart my systemd service that starts the dotnet runtime and runs the application.&lt;/p&gt;

&lt;p&gt;With Github Actions, this whole process can be automated by adding a single file to your repository. Each Action consists of a &lt;em&gt;workflow&lt;/em&gt; definition file that is placed in the &lt;code&gt;.github/workflows/&lt;/code&gt; folder. The workflow is composed of a number of actions that either run a script directly or execute a Docker container.&lt;/p&gt;

&lt;p&gt;You can add a workflow directly in Github if you go to the &lt;em&gt;Actions&lt;/em&gt; tab of your repository. From here you can choose a number of &lt;em&gt;starter&lt;/em&gt; workflows for various development tools or create a new workflow from scratch. The great thing is the editor which allows you to quickly look up all of the different actions that you can use in your workflow from the marketplace on the left hand side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pm5-JfZN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/cqkalywa03fyobakt2sl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pm5-JfZN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/cqkalywa03fyobakt2sl.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The workflow file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v1&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run a one-line script&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo Hello, world!&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run a multi-line script&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;echo Add other actions to build,&lt;/span&gt;
        &lt;span class="s"&gt;echo test, and deploy your project.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The file defines when the action is triggered and what steps are run.&lt;/p&gt;

&lt;h2&gt;
  
  
  My build workflow
&lt;/h2&gt;

&lt;p&gt;I am publishing a .NET Core MVC app, so I need to run the following when a commit is made to my &lt;code&gt;master&lt;/code&gt; branch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checkout my repository&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;dotnet publish&lt;/code&gt; to build my application&lt;/li&gt;
&lt;li&gt;Copy the files to my server &lt;/li&gt;
&lt;li&gt;Restart the service on my server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So first we set up the script to run when a commit is made. but we want to limit it to just the &lt;code&gt;master&lt;/code&gt; branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next in the &lt;code&gt;jobs&lt;/code&gt; section we are going to add our job and define what virtual machine OS will be used to run it. Github provides runners for Linux, Windows and macOS. Seeing as I am a Linux guy, I'm going to use that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next we add each step we need to do to deploy our application as listed above. This is set up in the &lt;code&gt;steps&lt;/code&gt; section. First we checkout the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then we need to setup .NET Core and build our application. Pay attention to the version of .NET you need - this must match your application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup .NET Core&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-dotnet@v1&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;dotnet-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2.2.108&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dotnet Publish&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet publish src -c Release -o deploy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This has build and published our files to a &lt;code&gt;/deploy&lt;/code&gt; directory on the virtual machine. Next is the good bit! We use a couple of great third-party commands from the market to copy the files using SCP to the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Copy via ssh&lt;/span&gt;
      &lt;span class="s"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;garygrossgarten/github-action-scp@v0.5.3&lt;/span&gt;
      &lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;local&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/home/runner/work/deferat/deferat/src/deploy/&lt;/span&gt;
        &lt;span class="na"&gt;remote&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_TARGET }}&lt;/span&gt;
        &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_HOST }}&lt;/span&gt;
        &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_USER }}&lt;/span&gt;
        &lt;span class="na"&gt;privateKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_SSH_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It's very important that we don't commit passwords in code to our repository (including our workflow definition - it is code after all!) as they can then be read by anyone! So we use the &lt;em&gt;Secrets&lt;/em&gt; tab in the &lt;em&gt;Settings&lt;/em&gt; tab of the repository. Any variables added here will be available in the script int he format &lt;code&gt;${{ secrets.SECRET_NAME }}&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CXI-VXHk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2ti87c6a0j3v4xipdidt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CXI-VXHk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2ti87c6a0j3v4xipdidt.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally I'm going to run a command over SSH to restart the service I have on my server. The command to run is &lt;code&gt;service deferat start&lt;/code&gt;, so we do this like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run SSH command&lt;/span&gt;
      &lt;span class="s"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;garygrossgarten/github-action-ssh@v0.3.0&lt;/span&gt;
      &lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sudo service deferat start&lt;/span&gt;
        &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_HOST }}&lt;/span&gt;
        &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_USER }}&lt;/span&gt;
        &lt;span class="na"&gt;privateKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_SSH_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The workflow is now complete! Once the file is committed (straight onto the &lt;em&gt;master&lt;/em&gt; branch of course 😄), this will trigger the action. Now when you go to the Actions tab, you can see a log every time the workflow has been run and you can see a detailed log of each step. Each step can be expanded to see more information.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eeLjoxAr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/uqa2f44hyk4ue69hbxn7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eeLjoxAr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/uqa2f44hyk4ue69hbxn7.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final &lt;code&gt;main.yml&lt;/code&gt; file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v1&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup .NET Core&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-dotnet@v1&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;dotnet-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2.2.108&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dotnet Publish&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet publish src -c Release -o deploy&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Copy via ssh&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;garygrossgarten/github-action-scp@v0.5.3&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;local&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/home/runner/work/deferat/deferat/src/deploy/&lt;/span&gt;
        &lt;span class="na"&gt;remote&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_TARGET }}&lt;/span&gt;
        &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_HOST }}&lt;/span&gt;
        &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_USER }}&lt;/span&gt;
        &lt;span class="na"&gt;privateKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_SSH_KEY }}&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run SSH command&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;garygrossgarten/github-action-ssh@v0.3.0&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sudo service deferat start&lt;/span&gt;
        &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_HOST }}&lt;/span&gt;
        &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_USER }}&lt;/span&gt;
        &lt;span class="na"&gt;privateKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_SSH_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And there it is! A simple file and you have your application building and deploying directly to a server 🎉&lt;/p&gt;

&lt;p&gt;See it in action at my blog repository here: &lt;a href="https://github.com/euronay/deferat"&gt;https://github.com/euronay/deferat&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>devops</category>
      <category>github</category>
    </item>
  </channel>
</rss>
