 So let me head over here to Ed. Now we're going to be talking about things with Azure DevOps. Now Azure DevOps lets you do all kinds of things to help make your projects better. Now I've been working on some projects on stream and I haven't written my code in a way that works really well with continuous integration or pipelines. Ed, can you help me out with that? Why don't you let folks know who you are and let's see what we can do to work on this. Sure, sure. Yeah, so my name is Edward Thompson. I'm a program manager at Microsoft. I work on the Azure DevOps team and my focus is kind of two areas. One is Git and version control and the other is Azure Pipelines, which is our continuous integration and continuous delivery system. Now I had a question earlier today from someone. Is there a difference between continuous delivery and continuous deployment? There's a lot of these terminologies that kind of get muddled and thrown around. Some people will say continuous integration is really only when you're building the master branch and not pull request validation. And some people kind of lump the continuous integration term to mean both things. And then continuous delivery and continuous deployment, again, gets a little questionable, like what is a deployment? If you go to a staging server, is that continuous deployment? If I check in and it automates the deployment all the way to staging, is that continuous deployment? Does it have to go to production? So these are all good questions. I think continuous delivery is what all those shipping folks are gonna be doing here through the holiday season. Just continuous delivery. That's right. So I've shared with you a little bit about a project I've been working on on my Twitch stream, where I've been building some tools for streamers to be able to build extensions, to use web pages and things to enhance their interaction on their stream. And I've never gotten Azure Pipelines to build quite right for it. And I started building it so we could go into a Docker container. Right. What are the steps I can take to make that happen so it builds and gets into that container? And then it'd be really great if I could deploy it. Well, let me show you. All right, let's do it. So actually, I've got your project right here. Okay. It's here on GitHub. C sharp fritz slash fritz.streamtools, this is the one, right? That's the one, that's the violator. Right. And it's an open source project. Everything's open source, absolutely. And I know that because I actually contributed a change to it. Yes. I opened a pull request yesterday. Really pleased about that. And you took it. Of course. Oh, well, you know. Of course. My code isn't always the best. That's why they made me a program manager, right? Oh man. So what I'm gonna do is I'm going to, the first thing I'm gonna do is fork your repository. So I'm gonna make a copy of it in my account. I don't mind. Go ahead and take a copy. So I'm just gonna click fork. And I've already, like I said, I've been working on this a little bit. Practicing. Yeah, exactly. So I'm gonna find this one. I've got this, this is a GitHub organization I set up just to manage this. I'm gonna fork it right into there. Just takes a second. I love that little image. Yeah, we're copying it. It's the old copy machine. That's right. And then after that, the easiest way to get set up with Azure Pipelines is through the GitHub Marketplace. So Azure Pipelines actually has a GitHub Marketplace extension. And that is by far the simplest way to get started. So all you have to do is scroll over here to the continuous integration section. Okay. Click there. And you will find Azure Pipelines right there. Oh my gosh. So get started. And of course, it's totally free for open source projects. So all I have to do is hit set up a new plan. I've got this free plan, that's great. That's all I need. And I select the account. Like I said, I've got a couple of GitHub organizations. So I've already got it set up. So I'm gonna select this one, the one that I just forked into. Just forked into. That's right. And then just click install it for free. So no charges for open source projects. Totally free, unlimited build minutes per month. And all I have to do is click complete. Once I do, it'll bring me over to Azure Pipelines. I'm gonna select the repository that I wanna use. That's the one I just forked. Click install. It's gonna make me enter my password just to confirm. It's gonna make me log in to Azure DevOps. Click that account right there. And so if you've never used Azure DevOps before, you can go ahead and get started, create a new organization. I'm gonna reuse one that I've already set up. I went ahead and set one up for today. And the reason that I did that is so that I could create a project. And so that I could, over here, this is Azure DevOps. This is the project I created. What I needed to do is add some secrets. Didn't wanna type them in here. You knew probably not a good idea. Right, all of a sudden I'll be Bitcoin mining before the end of the talk. So I can't tell you how many times I've accidentally leaked a key on my Twitch stream. Right, I totally believe it. So what I've done is I've gone ahead and set up a set of variables that we can reuse later that have those secret keys in there. Are those variables that are available throughout my build? They are, they are. Azure Pipeline sets up a number of variables itself. But you can also add variables either, right here is the easiest way to do it. This is a library. I can just give variables names. Some of them are just normal variables. Some of them are secure. So this won't actually show up. There's no way to see this. You won't see it in the pipeline. You can't echo it out, things like that. And that's the password for the container registry that we're gonna publish to. So again, don't wanna leak that out. Right, so here we are. So GitHub's taking us to Azure Pipelines and we're gonna go ahead and set up. We're just gonna select the repository we wanna build. And when we do, Azure Pipelines is actually gonna analyze it and look at the code that's in there and try to come up with the best template. Best match. Yeah, all right. And it looks at your code, notices it's a .NET project. Suggests that we build a .NET app. I don't wanna do that though. Okay, okay. I want to build a Docker image instead. And that's one of the other templates we have. Yeah, all right. Cause you actually wanna do this nice modern deployment system. You wanna build your .NET core app into a Docker image. You wanna deploy the Docker image. And so we've got first class support for that as well. Yeah, my thinking is if this is a set of tools that folks that are running streams want to use, put down your own little image, configure it with everything for your stream, for your project and run it wherever you'd like on your local machine, in the cloud, wherever it makes sense to you. Yeah, I think that totally makes sense. I love that sort of a setup. But it's not something that I'm super familiar with. So we're gonna hope that I don't screw anything up as we go. Cause the code I write is like old school native code these days. Okay. So this new fangled modern world I'm not super familiar with. So let's see if we can get it right. Modern world of YAML. Of YAML, that's right. That's right. And here we've got some YAML. Yeah, so listen, if you're one of our friends out there hanging out in the chat room over there on Twitch, make sure you send over your questions that you might have for us about Azure Pipelines and or anything about the project that we've been working on and the moderators will pass them along to us and we'll be happy to answer them. Absolutely, absolutely. So right, so this is the default YAML for building a Docker image. I just wanna make a few tweaks to it based on what you want. So first of all, the first thing we wanna do is select where we're gonna do the build. We support cloud-hosted builds for Linux, for Windows and for macOS, but for a Docker sort of setup, I think Ubuntu is obviously the right way to go. Nice one to use. Yeah, so second, here's the variable section. So I can define variables here also that will be used throughout the build process. And I wanna change this a little bit. I wanna use the array syntax instead. So I'm going to set a variable that has a name of image name. So I'm just gonna change this and a value. We're gonna change that from your container image name. We're gonna call it fritz.streamtools. And we've got a typo in there. I do, thank you. Pair programming. Pair programming indeed. And then I'm gonna bring in that group of, that variable group that I defined earlier. So that's got all the Azure secrets. So now I can actually refer to those. Do we need a hyphen in front of value? No, I don't. So it's name, value. I don't know my YAML. I'm learning, but I think this is right. I think this is right. And then I'm going to set the Docker file that we wanna build. And that should all be good. So that's gonna run that Docker file, build that Docker image and tag it with that. And once that's built, I wanna do one more thing. I wanna take that Docker image that's built and publish it up to my Azure container registry. Okay, so we have a private registry for our images that it's our working place. It's not public, but it's someplace that we can share these containers from. That's exactly right. And that's why I needed that username and password. I could publish up to Docker hub. That's no problem. I'd probably need to authenticate there too. But I want to, there we go. I wanna publish to my Azure container registry. So the first thing I need to do is run Docker login and then I can Docker tag that image again. So it's got that tag. It's gonna be called fritz.streamtools. I also wanna tag it with the container registry in the name. Oh yeah. And then I can Docker push that image. If I can type it, there we go. All right. So, and let's give it a display name so that it looks nice when we see it. Okay, so yeah. So we're gonna build this Docker image. We're gonna push the Docker image to our private container registry. Let me just make one, take one more look, make sure. Nope, I missed closing parentheses. There we go. So that all looks good. We'll authenticate, we'll tag it, and then we'll push it. So when I click save and run, gonna kick off our build. Okay, that's too quick for me. My gosh. All right, display name that you put down there at the bottom, that's the display name for this step. Yeah, for this job, for this step in the build task. That's right. And I get the scripts, that makes sense. I'm used to seeing those because I'll run them locally on my machine so that I can push to container registry. Yeah, that's exactly right. The way I think about continuous integration builds is that I want it to be as similar as possible to the development build. Because that's familiar. Sure, now Kirsten has a good question in the chat room. What if I wanted to add unit tests into this and fail in the middle of it? Is that just another step? That's just another step, yeah. I mean, you can just run whatever commands. You could run .NET, .NET test, that's exactly right. And then run the test, and then if any of them fail, it gets a non-zero return code, it'll stop. It'll stop. Fantastic. That's exactly right. And then you can even publish the build, sorry, the test results up into Azure Pipeline so that you can actually examine the tests and look at, kind of drill down and see what failed. Oh, and the program manager doesn't love test results and numbers, the meters and things that they can look at. I do love all of that. So when I click Save and Run, what it's gonna do is it's actually gonna take this YAML, check it into my repository on GitHub. That's why I forked it, because I can check into yours. At least until, you know, you give me that commit. Until I accept and give you that commit. So I'll click Save and Run. And what that's gonna do is it's gonna kick off our first build. So it's gonna create the first Docker image and upload it. Okay, and it uploads to the registry. Now, eventually, I'll be able to push that out to an Azure, right, I'll be able to do a release and push that to an Azure app service or a container service. Right. Now, Kelcomnet in the chatroom is asking, they're currently using Jenkins to build and deploy to servers behind a firewall. Can DevOps deploy to those on-premise? It absolutely can, yeah. Okay, not just to Azure. Not just to Azure. That's one of the really cool things about Azure Pipelines is you can kind of mix and match on-premises and cloud, however you wanna do it. So, you know, we have build agents in the cloud for you for, again, Linux, Windows and Mac OS. So you can take advantage of those. But if you have some infrastructure on-premises that you wanna use, maybe you've got some complicated dependencies that you need to set up and you wanna do it once on your computer and then hide it under a desk and never touch it again. Oh, yeah. And you can install the build agent on that. And so you can actually build with a mix and match of on-premises and cloud build agents. But the same is true for release. So you can release either to a cloud, whether that's Azure, which I hope it is. You know, I like Azure. Absolutely. Or somebody else's cloud. Somebody else's cloud is fine, too. Or on-premises infrastructure that you already have. That's no problem at all. Awesome, yeah. Okay, so I'm assuming I would have to install a little piece of software on my on-prem stuff so that it can be visible to the DevOps so it can publish to it. That's right. I got you. That makes sense. While we're waiting for that, Guatello, I hope I'm pronouncing that correctly. How do I set up multi-tenant deployment with the main repository and a different selection of plugins from another repo for each tenant? Oh, wow. That sounds really complex. That's some advanced stuff. The nice thing though is that, yeah, you can, that YAML is very, very flexible. And so you can, in fact, just run some Git commands. If you have a deployment to go to, say, production and you want to bring in other plugins from another repo, you can just run Git clone. Okay. You can use submodules. You can use Git LFS. So you've got a lot of options on how you wanna orchestrate your source code and then send it off to deployments. And so you can decide which builds are gonna run based on the deployment target, for instance. Okay, okay. That makes a lot of sense to me. So, I'm sorry? No, no, I was gonna say with the different ways that you can pivot and write the code there, I guess you can set up that logic to run different sets based on outcomes. That's exactly right. And you can do a lot of conditions. If this step fails, move to this step. If this step succeeds, go to this step instead. So you can really just build a whole graph of the directions you wanna take. You can fan out, you can fan back in, wait on different resources. So you can kick off some builds in parallel, some steps in parallel, and then wait for all of them to finish before collecting all their output, zipping it up or whatever, creating an artifact from it and moving on. I'm thinking of things like Xamarin apps where I might kick off parallel builds, one for here's the iOS version, here's the Android version, and then at the end, collect them together and publish the results. Yeah, that's a really cool idea. One thing I see people do is they've got a backend that's.net core, they've got a front end that's TypeScript, and so kick off the builds for each of those in parallel. Oh, very cool. Yeah. Okay, okay. Now, Kirsten has another question for us. Ooh, looks like we've got some results. We do. Let's get Kirsten's question first. Can I access an on-prem database with my unit test? I'm guessing Kirsten's referring to that are running on Azure DevOps. That feels like a firewall question. Yeah, yeah, that's exactly right. I would not do that myself. No. But what I would actually do is then move my build into a non-premises build agent. Okay. And that way you can feel a little bit more comfortable about the security separation that you're making. Oh, yeah. I've actually seen some folks where they'll create a Docker image that's using Postgres or some other database, and they seed that image with whatever test data they want so that when they do wanna run their tests, they pull that test database and it's seeded with that stuff. And they connect to that. That way there's no risk of damaging something behind a firewall or on production. Right, right. Yeah, that makes me feel a lot better than touching production with a unit test. Yes. All right, so what do we got? What do we got? Right, right. So what's happened is, so we've done the Docker build and what that does is it pulls down all your layers and sets everything up. And then we go ahead and install it. We do the .NET restore, we do the .NET build, and that's that. And then we've got a Docker image. Very cool, right? Yeah, yeah. So that, and that's just stuff that you already had. You had your Docker file set up in your repository. Yeah, we spent some time working on that previously and it's been running great for me locally, but you've already got it published out to a registry. I do, I do. And so why don't we take this then and let's deploy. Okay. So we've got an Azure web app ready to go. To receive it. I do. Yeah. It's called, man, it's like Christmas. It's on some demo.azurewebsites.net. And I think all that's there now is, yeah, hello world. Right? Let's zoom in on that. This is the simplest web app ever. This is, this is live coding. Yeah. On to it. Yeah. If I view source, you won't even see an HTML tag. It's, it's text printf hello world or nice. Yeah, yeah. So let's set up a release. And so the release pipelines functionality in Azure Pipelines is also incredibly powerful. So I'll just click on that, create a new pipeline and I'm gonna select Azure App Service Deployment. Sure. That's where we want to go. Absolutely. But again, this could be an on-premises sort of setup. You can, you know, deploy to IIS. You can deploy to other clouds. It doesn't have to be Azure. Sure. Okay. But Azure is the one that I prefer. So, and we can set up all sorts of crazy stages. And again, if you saw Donovan's keynote fanning out, fanning in, very complicated, we're gonna have one. Sure. We only need one stage right now. We're gonna call it deploy to production. And what we're gonna do is we're gonna tie this deployment, this release to the build that we just did so that we can grab the build number out of the, that got published. The completed Docker image that was published. That's right. That's right. Okay. So, you can tie it to all sorts of artifacts so that you can trigger basically when a GitHub repository gets updated. But we want to tie it to a build. Sure. And every time I start walking through continuous integration or continuous deployment, these visions of the mousetrap game go through my head. You know what I'm saying, right? Yes, I do. It's like, I know how to put this together, but do I need the bathtub with the guy flipping into it? Or no. Maybe you do. Maybe I do. Okay. So. That's part of Azure now. It's the bathtub flipping guy. Yeah, the best. Yeah, definitely. I think we announced that today. Oh, fantastic. Yeah. Guy in a red shirt mentioned it. So I'm gonna, so I'm gonna actually use a newer version. This is actually the preview version of the app service deployment. And I'm going, because that's the one that I think has the best functionality and the best support for containerized deployments. Okay. And that's exactly what we want, right? So I've got an Azure subscription. Oh, I think I need to authorize it. So I'm gonna have to log in really quickly just to tie the Azure DevOps account to the Azure portal. Right. Because DevOps, while it has the Azure name, isn't the same as your hosting Azure plan. That's exactly right. For all your Azure services. There we go. I'll stay as I connect them manually. That's right. So you just connect them up. Because yeah, you could, you might want one Azure DevOps account to talk to a different Azure portal account. Okay. Now, if I do have other people in my organization, I wanna make sure that they can't see or touch those subscriptions. They're prevented from seeing that, right? That's tight. Yeah, that's, once the authorization's done, you can do that as an administrator. You don't have to do, you don't have to mess with it ever again. Nobody can do anything. So now that's all linked up. And so, yeah, that subscription is now nice and locked. And so I can say, again, we've got all sorts of options. You can deploy a mobile app. You can deploy Azure functions. I wanna do a web app for containers. And I'm gonna do it to a Linux host. And once I select that, it actually figures out all of the web apps that I have that satisfy that condition. Again, eTompson demo is the one I wanna use. Registry. So this is our container registry that we set up. That we published to in the previous build. That's right. Okay. And that's a fully qualified URL. And then the image that we wanna use is fritz.streamtools. And we'll use the build ID. So we basically, we published a build ID. That's the tag in the Docker image. And we'll just reuse that. Grab it and carry it along to this process. That's exactly right. That's why we linked the build previously. So that we could use that build ID. And no- That output information. That's exactly right. That's exactly right. So now we save it and then we can create a release. It's that easy. Oh, man. And we can watch it go just like before. It'll take just a little bit of time to spin up a host and start doing the deployment. Okay, there we go. Two tasks. And it'll find that third task. And so this is actually the money task. This is the one that's actually doing the deployment. Copying that image out and spinning it up. That's right. Oh, man. That is so cool to see. Yes, right? Because it's like I'm a right-click deploy guy for how many years. I know. I know. I'm sorry. I'm sorry out there. My buddy Damien is crying right now. I hope he's watching and I'm sure he's saying friends don't let friends right-click publish. Back in the day, before continuous integration really became a thing. Right. We did right-click deploy and we had a folder on disk. We zipped everything up and then we took that zip file. We handed it off. You know what I'm saying. I do know. We handed it off to our friend in operations and we both come in at three in the morning and we take that zip file and put it out on the appropriate server and it was blessed and we ran our smoke tests. And hopefully we got to go to sleep by seven in the morning because everything worked. Seeing the deployment like this, it's, I wanna cry. It's like 15 years ago, this was a pain in the neck to do. Oh yeah, oh yeah. You'd get the whole team together in the conference room. You'd buy in pizza because you had to. And you print it out word docs. This is all the steps. Now, Ed, you're gonna do these steps and Jeff, you're gonna do these and Ron, you're gonna have these and everybody had, here's your mission that you have to do and it's three in the morning and you're all bleary eyed and you're praying that it works. Right. It's all totally manual. So many things that can go wrong. So many typos that you can make, especially because it's three in the morning. It's three in the morning. Yeah, no, you gotta automate all that stuff. Yes. Oh my gosh. Yeah. So this should be just about done. We'll give it, we'll give it one more minute. Okay. And we have a question from, is that KLabran or is it KLabran? Can the YAML be run locally or should we look into using a build script? The devs can run locally and the YAML file calls into its steps. Right, right. So right now the YAML can't be run locally. I think that we're working on basically a local executor so that you could run the YAML locally. Kind of like, kind of like how I have your storage emulator locally. It's exactly right. Yeah. But I don't have anything to announce there. For sure. But it's a concept that is being explored. That's right. But what I like to do is actually do have the build script. That way I can run it locally. I can debug it locally because spinning up a new build is quick but it's not instantaneous. It's so much quicker if I don't have to do that. If I can just do it locally and find out what's wrong with my build script. So I can do that debugging. And then I have a build step that is just run the script. Okay. But not everybody agrees with me. And so there's healthy debate on all sides. Well, and we just did that here, right? We ran the build script that built using the Docker file to actually compile and give us the image. That's exactly right. And so we had a good feeling that it was gonna work fine because we'd both run this Docker file already. We just had to get version numbers lined up, right? The chase of the version. Yeah, I know. Version numbers. That is tricky, isn't it? Yes. So we're moving along? Yeah. So that is still deploying. Sometimes it takes a little while longer. But right, hopefully, if you are running this for test or you're running it for production, right? You're not sitting there waiting with bated breath for it to be deployed. No, no. You'll just get an email when it's done. Okay. Right. Usually you're not on camera. Sweat in the minutes. No, we're not on camera. Sweating, anything like that. So, right. Okay. So, hey, while that's going, why don't we take a look at what it actually set up in the GitHub repository? Because it's actually done some cool things. Okay. And this is why you were saying that you needed to fork and have your own version. You needed to commit. What did it put into the GitHub repository? That's right. Let's take a look. I don't have a temp for that. Let me go open it. There we go. So yeah, this is the forked version. And the first thing you'll notice right here is that there's been a new commit by Azure Pipelines. Okay. And so what it's actually done is it's set up that yaml file, azurepipelines.yaml right there. And this was what we modified. That's exactly what you did in the web browser. That's exactly right. Okay. And so now I can just keep making changes. I can just get clone this repository. I can do it right here if I want to make changes to the way the build process works and it gets checked in. It's called configuration as code. The idea is that you want the way that your code gets built versioned alongside the code itself. That way, as you change your build process, it gets reflected there. So I can roll back to a previous version and I've got the old build process that reflected that. Or even if I forked create a branch because I'm building a new feature or I'm building a new project that maybe I'm adding a Xamarin application to this or I'm adding, in our case, we're building a Visual Studio extension. Well, I need to change the build steps because there's now a step to build with Visual Studio. That's exactly right. But you don't want that in the master branch because that code doesn't exist yet. Okay. Right. So this is really powerful. The other thing that it's done is it's set up some web hooks. So now, anytime somebody creates a pull request, it will actually run this build validation. It will run our continuous integration build. So if I make a change to the readme, maybe I want to clarify this. What does OBS stand for? Open Broadcaster Software. Right. So maybe I want to call that out so that people who aren't familiar with it will know. Sure. I can open a pull request. Pull request. There we go. And there we are. And now what you'll see is we've got this check field here. So what it's actually doing is every time somebody opens a pull request, it will go to Azure Pipelines to start a build for this change that we made right there. Oh, very. Now, okay, that's pretty cool that it's running the build, but it's running the build for the readme. Is there a way that I can put a filter on that so that it doesn't run for markdown files? Yeah. So you can, in your build script, you could like maybe take a look at what you wanted to do. Would be the easiest way to do that, I think. Because you might want to do something with markdown files. You might want to make change. You wouldn't want to do a full code build necessarily. No, absolutely not. But you might lint the markdown. Ah, okay, okay. Now, you set that up to trigger whenever there's a pull request. There's a question in the chat room asking, well, can I schedule this perhaps based on a cron? Sure, absolutely. Yeah, so you can set up on, so this is our, yeah, this is our continuous integration build. It'll build every time somebody pushes something to master, I'm sorry, dev. Your default range is dev. Yep. Somebody pushes something to dev on GitHub. Okay. Or every time somebody opens a pull request, I could also create a new build pipeline that runs maybe at a certain time. Right, so I can, let's just go ahead and select that yaml build. And I've got all these triggers here now. So the default one. Oh, it's right there. Yeah, so the default one is to trigger based on continuous integration check-ins, but I can add a scheduled build to run it as well. Very straightforward. There you go, chat room. And you gotta watch out for these time zones, man. Now that, so I live in the UK, and so I've got some continuous integration builds that kick off at midnight every night, which is a little bit different when you're here in Las Vegas. When you're in the States. Yeah, I was like specific time, what, that's nine hours ahead? Yeah, I was like, why are all these builds running all of a sudden? So pro tip, time zones are terrible. They're not terrible, they're confusing. They're confusing. So, okay, so while we were looking at that, it turns out that our deployment finished. So let's take a look at what happened. It ran and updated to point to my container registry and fritz.streamtools number 77. So let's actually go load up that website. So it was hello world, we're gonna hit reload. And hey. Oh man, look at that. So, this is your screen tool? Yeah, that we've been building, right? I've got that current viewers widget that I use and the current followers and those are just pulling test data now because we haven't registered any information. So where you had put variables in for deploying to the container registry, I have some configuration information that we put into our app settings around like my Twitch credentials. So that the bot can pull down and appropriately access my channel and pull that information. Otherwise, I just have a test piece of data that updates occasionally so you can see that it works. But we can do things like, if we go up to, if we go to follower goal up top there and pull that down, configuration, and this gives my, so this is a little configuration thing until we fill out some of these, it'll reload that preview and give us the full information, a little bar, right? One of those gauges, everybody likes to have a thermometer. I'll close the way to goal. So that's what this will do. Cool. So, oh my gosh, it works and does the thing. Now, when I'm ready to deploy the next version, right? When I finish my put my, when I get ready my next feature and I'm ready to deploy that, will I always be triggering the release or is there something that I can do to say only trigger the release when this happens, right? That you pointed out particularly, and I'm thinking out loud that I use that dev branch. This is where I'm working, but master is my production quality. Right, because you use get flow. Yes. I love a conversation about that later. Oh no. I've been a bad developer. Anyway, I'm sorry. Not a problem. I didn't mean to interrupt you. No, no, please. Oh my gosh. So much trouble. So what we've done is the release that I set up is triggered manually right now. So there's no sort of automation yet. But what we could do is we could set up, just like you had build triggers, we can trigger a release based on a build. So every time we do a continuous integration build, we're gonna upload something to the container registry. Every time that happens, we could trigger a release to a staging server. So I could do that smoke test. Make sure that it loads up and has, in my case, my Twitch configuration loaded properly for this. That's right, that's right. So what we did is we just added this one stage, deploy to production. I could add a new stage and let's call it staging. Let's see if I can remember how to set the, here actually, no, that's not what I wanna do. Let me get rid of this. And I love how flexible, because I've built this with ASP net core, it's just a little website. It's an easy sample that we can push around like this and how flexible the DevOps tools are at picking up and just run with it and let's deploy and package it. Right, so, and let me change this. So, and this is something I actually do with my blog, which is perhaps a bit of overkill. So what's gonna happen here is the first thing that happens in this deployment, and this is the sort of thing that you might have for an automated continuous deployment sort of setup, is it's gonna deploy to staging. And then I can add some approval gates. So I could add myself, for instance, as an approver here. So now we've actually need a manual approval before we go to production. Yes, so it'll go to staging all the time. Every change you make goes to staging because it's a staging server, who cares? Right, but production, of course, is a little bit more important. Yeah, so what you can do is you can go to the staging server, validate that everything's working correctly, and then deploy to production when you're happy. Very cool, okay. So that was a manual process. Is there, I'm assuming there's also automated process. Sure, automated gates. Sure, sure. Yeah, so I mean, the most obvious one again is you could do a time-based trigger, you could do a trigger based on a build success. Okay, okay. Could I run integration tests with something like Selenium? And based on the results of those tests, decide, oh, yes, here's my automated smoke tests deployed. Yeah, absolutely. You can even do something that, let me click on that again and edit it. So the, there we go. Oh, I didn't save that. Anyway, there are all sorts of these deployment conditions. So you can do gates. So here's a really naive one. Five minutes after the staging goes, go to production. If nobody's running screening, then you don't cancel it and it'll just automatically go. That's pretty naive. But the more interesting things I think are, yeah, you can hit a REST API, you can run an Azure function. So something somebody did was Twitter Cinema Analysis. Oh. Wiring up Twitter Cinema Analysis. So you can roll back a deployment if people on Twitter are complaining about it. Ha, ha, ha, ha. So there's all sorts of clever things that you can do. Maybe if somebody files a bug against you, you roll back. So that's query work items. I could put a command in my chat room and moderators could roll back. Ha, ha, ha, ha. That's right. That's dangerous. It is, it is. Yeah, so there's all sorts of clever ways that you can build your workflow. Very cool. That's what it is. It's a very customizable workflow engine. And there's notifications hooked up to these steps, right? I'm not just gonna suddenly be like, oh, guess what? Version 78's in production. Right, no. You'll get an email. And in fact, the email that you get when we had that staging to production setup, you'll get an email and you can just click button to approve if you're the approver that's notified. Okay, and then the deployment process happens and as we saw two, three minutes later, it's just up and running. Does it gracefully deploy and swap that out? Sure, you could do blue-green deployments. You could do some sort of DNS update or it's incredibly flexible. Staging a lot and swap. You could do a, what do they call that in Azure? You could swap a slot, yeah, that's exactly right. Yeah, the staging, whatever. Right, I don't wanna get the name wrong. Right, because I'm going to. Same, same. But yeah, I love a blue-green deployment. It's probably overkill for where you are today, but I think you're gonna get there. There's a question here in the chat room, speaking of integration tests, like we're talking about, because I really wanna make sure that I deploy this properly. Do the DevOps agents have SQL Express or MS Local DB or SQL Server Developer Edition if they have to work with a database? Right. Do they have one of those? Right, the agents themselves do not have a database, but you could talk very easily to SQL Azure or something. Or this is an example of where you might wanna run your own agent locally. And of course, it doesn't have to be locally in your data center. It could be something that you've provisioned in Azure yourself. Now, I'm gonna go to the last question from Kirsten. Kirsten's been pretty engaged during this session. Thanks so much for joining us. Can I use a deployment group to deploy to an on-prem test and then only deploy to on-prem production if the test pass? Yeah, that's some advanced stuff. But yeah, absolutely. It's really just a very flexible workflow engine. So you can just wire it up to if this, then that, if this, then that. Very cool stuff. Oh my gosh, Ed, thanks so much for helping me out with this. Thank you. I've gotta get together with you about this. Afterwards, I know I'm gonna get my hand slapped. Don't tell Donovan.