├── ARM
├── azuredeploy.json
└── azuredeploy.parameters.json
├── DevOpsLab1.md
├── DevOpsLab2.md
├── DevOpsLab3.md
├── DevOpsLab4.md
├── DevOpsLab5.md
├── DevOpsLab6.md
├── DevOpsLab7.md
├── README.md
└── images
├── AI_1.png
├── AI_10.png
├── AI_11.png
├── AI_12.png
├── AI_13.png
├── AI_14.png
├── AI_15.png
├── AI_2.png
├── AI_3.png
├── AI_4.png
├── AI_5.png
├── AI_6.png
├── AI_7.png
├── AI_8.png
├── AI_9.png
├── CD_1.png
├── CD_10.png
├── CD_11.png
├── CD_12.png
├── CD_13.png
├── CD_14.png
├── CD_15.png
├── CD_16.png
├── CD_17.png
├── CD_18.png
├── CD_2.png
├── CD_3.png
├── CD_4.png
├── CD_5.png
├── CD_6.png
├── CD_7.png
├── CD_8.png
├── CD_9.png
├── IC_1.png
├── IC_10.png
├── IC_11.png
├── IC_12.png
├── IC_13.png
├── IC_14.png
├── IC_15.png
├── IC_16.png
├── IC_17.png
├── IC_2.png
├── IC_3.png
├── IC_4.png
├── IC_5.png
├── IC_6.png
├── IC_7.png
├── IC_8.png
├── IC_9.png
├── Intro1.png
├── Intro2.png
├── L1_1.png
├── L1_10.png
├── L1_11.png
├── L1_12.png
├── L1_13.png
├── L1_14.png
├── L1_14a.png
├── L1_15.png
├── L1_16.png
├── L1_17.png
├── L1_18.png
├── L1_19.png
├── L1_2.png
├── L1_20.png
├── L1_21.png
├── L1_3.png
├── L1_4.png
├── L1_5.png
├── L1_6.png
├── L1_7.png
├── L1_8.png
├── L1_9.png
├── L1_A1.png
├── L1_A2.png
├── L1_A3.png
├── L2_1.png
├── L2_10.png
├── L2_11.png
├── L2_12.png
├── L2_13.png
├── L2_14.png
├── L2_15.png
├── L2_2.png
├── L2_22.png
├── L2_23.png
├── L2_3.png
├── L2_31.png
├── L2_4.png
├── L2_5.png
├── L2_6.png
├── L2_7.png
├── L2_8.png
├── L2_9.png
├── Lab1_1.png
├── Lab1_2.png
├── Lab1_3.png
├── Lab1_T4_S4.png
├── Lab2_T1_S1.png
├── Lab2_T1_S10.png
├── Lab2_T1_S2.png
├── Lab2_T1_S3.png
├── Lab2_T1_S9.png
├── Lab2_T2_S1.png
├── Lab2_T2_S5.png
├── Lab4_T1_S1.png
├── Lab4_T1_S3.png
├── Lab4_T1_S8.png
├── Lab4_T2_S3.png
├── Lab4_T2_S4.png
├── Lab4_T2_S5.png
├── Lab5_T2_S1.png
├── Lab5_T2_S12.png
├── Lab5_T2_S2.png
├── Lab5_T2_S3.png
├── Lab5_T2_S4.png
├── Lab6_T1_S3.png
├── Lab6_T2_S10.png
├── Lab6_T2_S11.png
├── Lab6_T2_S2.png
├── Lab6_T2_S5.png
├── Lab6_T2_S8.png
├── S_1.png
├── S_10.png
├── S_11.png
├── S_2.png
├── S_3.png
├── S_3_1.png
├── S_4.png
├── S_5.png
├── S_5_1.png
├── S_6.png
├── S_7.png
├── S_8.png
├── S_9.png
├── WA-1.png
├── WA-2.png
├── WA-3.png
├── WA-4.png
├── WA-5.png
├── WA-6.png
├── WA-7.png
├── WA-8.png
└── WA-9.png
/ARM/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "hostingPlanName": {
6 | "type": "string",
7 | "minLength": 1
8 | },
9 | "skuName": {
10 | "type": "string",
11 | "defaultValue": "F1",
12 | "allowedValues": [
13 | "F1",
14 | "D1",
15 | "B1",
16 | "B2",
17 | "B3",
18 | "S1",
19 | "S2",
20 | "S3",
21 | "P1",
22 | "P2",
23 | "P3",
24 | "P4"
25 | ],
26 | "metadata": {
27 | "description": "Describes plan's pricing tier and instance size. Check details at https://azure.microsoft.com/en-us/pricing/details/app-service/"
28 | }
29 | },
30 | "skuCapacity": {
31 | "type": "int",
32 | "defaultValue": 1,
33 | "minValue": 1,
34 | "metadata": {
35 | "description": "Describes plan's instance count"
36 | }
37 | },
38 | "location": {
39 | "type": "string",
40 | "defaultValue": "West US"
41 | },
42 | "webSiteName": {
43 | "type": "string",
44 | "defaultValue": "[concat('webSite', uniqueString(resourceGroup().id))]"
45 | }
46 | },
47 | "resources": [
48 | {
49 | "apiVersion": "2015-08-01",
50 | "name": "[parameters('hostingPlanName')]",
51 | "type": "Microsoft.Web/serverfarms",
52 | "location": "[resourceGroup().location]",
53 | "tags": {
54 | "displayName": "HostingPlan"
55 | },
56 | "sku": {
57 | "name": "[parameters('skuName')]",
58 | "capacity": "[parameters('skuCapacity')]"
59 | },
60 | "properties": {
61 | "name": "[parameters('hostingPlanName')]"
62 | }
63 | },
64 | {
65 | "apiVersion": "2015-08-01",
66 | "name": "[parameters('webSiteName')]",
67 | "type": "Microsoft.Web/sites",
68 | "location": "[resourceGroup().location]",
69 | "tags": {
70 | "[concat('hidden-related:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource",
71 | "displayName": "Website"
72 | },
73 | "dependsOn": [
74 | "[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
75 | ],
76 | "properties": {
77 | "name": "[parameters('webSiteName')]",
78 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]"
79 | }
80 | },
81 | {
82 | "apiVersion": "2014-04-01",
83 | "name": "[concat(parameters('hostingPlanName'), '-', resourceGroup().name)]",
84 | "type": "Microsoft.Insights/autoscalesettings",
85 | "location": "[resourceGroup().location]",
86 | "tags": {
87 | "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource",
88 | "displayName": "AutoScaleSettings"
89 | },
90 | "dependsOn": [
91 | "[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
92 | ],
93 | "properties": {
94 | "profiles": [
95 | {
96 | "name": "Default",
97 | "capacity": {
98 | "minimum": 1,
99 | "maximum": 2,
100 | "default": 1
101 | },
102 | "rules": [
103 | {
104 | "metricTrigger": {
105 | "metricName": "CpuPercentage",
106 | "metricResourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]",
107 | "timeGrain": "PT1M",
108 | "statistic": "Average",
109 | "timeWindow": "PT10M",
110 | "timeAggregation": "Average",
111 | "operator": "GreaterThan",
112 | "threshold": 80.0
113 | },
114 | "scaleAction": {
115 | "direction": "Increase",
116 | "type": "ChangeCount",
117 | "value": 1,
118 | "cooldown": "PT10M"
119 | }
120 | },
121 | {
122 | "metricTrigger": {
123 | "metricName": "CpuPercentage",
124 | "metricResourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]",
125 | "timeGrain": "PT1M",
126 | "statistic": "Average",
127 | "timeWindow": "PT1H",
128 | "timeAggregation": "Average",
129 | "operator": "LessThan",
130 | "threshold": 60.0
131 | },
132 | "scaleAction": {
133 | "direction": "Decrease",
134 | "type": "ChangeCount",
135 | "value": 1,
136 | "cooldown": "PT1H"
137 | }
138 | }
139 | ]
140 | }
141 | ],
142 | "enabled": false,
143 | "name": "[concat(parameters('hostingPlanName'), '-', resourceGroup().name)]",
144 | "targetResourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
145 | }
146 | },
147 | {
148 | "apiVersion": "2014-04-01",
149 | "name": "[concat('ServerErrors ', parameters('webSiteName'))]",
150 | "type": "Microsoft.Insights/alertrules",
151 | "location": "[resourceGroup().location]",
152 | "dependsOn": [
153 | "[concat('Microsoft.Web/sites/', parameters('webSiteName'))]"
154 | ],
155 | "tags": {
156 | "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', parameters('webSiteName'))]": "Resource",
157 | "displayName": "ServerErrorsAlertRule"
158 | },
159 | "properties": {
160 | "name": "[concat('ServerErrors ', parameters('webSiteName'))]",
161 | "description": "[concat(parameters('webSiteName'), ' has some server errors, status code 5xx.')]",
162 | "isEnabled": false,
163 | "condition": {
164 | "odata.type": "Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition",
165 | "dataSource": {
166 | "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource",
167 | "resourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/sites/', parameters('webSiteName'))]",
168 | "metricName": "Http5xx"
169 | },
170 | "operator": "GreaterThan",
171 | "threshold": 0.0,
172 | "windowSize": "PT5M"
173 | },
174 | "action": {
175 | "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction",
176 | "sendToServiceOwners": true,
177 | "customEmails": []
178 | }
179 | }
180 | },
181 | {
182 | "apiVersion": "2014-04-01",
183 | "name": "[concat('ForbiddenRequests ', parameters('webSiteName'))]",
184 | "type": "Microsoft.Insights/alertrules",
185 | "location": "[resourceGroup().location]",
186 | "dependsOn": [
187 | "[concat('Microsoft.Web/sites/', parameters('webSiteName'))]"
188 | ],
189 | "tags": {
190 | "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', parameters('webSiteName'))]": "Resource",
191 | "displayName": "ForbiddenRequestsAlertRule"
192 | },
193 | "properties": {
194 | "name": "[concat('ForbiddenRequests ', parameters('webSiteName'))]",
195 | "description": "[concat(parameters('webSiteName'), ' has some requests that are forbidden, status code 403.')]",
196 | "isEnabled": false,
197 | "condition": {
198 | "odata.type": "Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition",
199 | "dataSource": {
200 | "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource",
201 | "resourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/sites/', parameters('webSiteName'))]",
202 | "metricName": "Http403"
203 | },
204 | "operator": "GreaterThan",
205 | "threshold": 0,
206 | "windowSize": "PT5M"
207 | },
208 | "action": {
209 | "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction",
210 | "sendToServiceOwners": true,
211 | "customEmails": []
212 | }
213 | }
214 | },
215 | {
216 | "apiVersion": "2014-04-01",
217 | "name": "[concat('CPUHigh ', parameters('hostingPlanName'))]",
218 | "type": "Microsoft.Insights/alertrules",
219 | "location": "[resourceGroup().location]",
220 | "dependsOn": [
221 | "[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
222 | ],
223 | "tags": {
224 | "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource",
225 | "displayName": "CPUHighAlertRule"
226 | },
227 | "properties": {
228 | "name": "[concat('CPUHigh ', parameters('hostingPlanName'))]",
229 | "description": "[concat('The average CPU is high across all the instances of ', parameters('hostingPlanName'))]",
230 | "isEnabled": false,
231 | "condition": {
232 | "odata.type": "Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition",
233 | "dataSource": {
234 | "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource",
235 | "resourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]",
236 | "metricName": "CpuPercentage"
237 | },
238 | "operator": "GreaterThan",
239 | "threshold": 90,
240 | "windowSize": "PT15M"
241 | },
242 | "action": {
243 | "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction",
244 | "sendToServiceOwners": true,
245 | "customEmails": [ ]
246 | }
247 | }
248 | },
249 | {
250 | "apiVersion": "2014-04-01",
251 | "name": "[concat('LongHttpQueue ', parameters('hostingPlanName'))]",
252 | "type": "Microsoft.Insights/alertrules",
253 | "location": "[resourceGroup().location]",
254 | "dependsOn": [
255 | "[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
256 | ],
257 | "tags": {
258 | "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource",
259 | "displayName": "LongHttpQueueAlertRule"
260 | },
261 | "properties": {
262 | "name": "[concat('LongHttpQueue ', parameters('hostingPlanName'))]",
263 | "description": "[concat('The HTTP queue for the instances of ', parameters('hostingPlanName'), ' has a large number of pending requests.')]",
264 | "isEnabled": false,
265 | "condition": {
266 | "odata.type": "Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition",
267 | "dataSource": {
268 | "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource",
269 | "resourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]",
270 | "metricName": "HttpQueueLength"
271 | },
272 | "operator": "GreaterThan",
273 | "threshold": 100.0,
274 | "windowSize": "PT5M"
275 | },
276 | "action": {
277 | "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction",
278 | "sendToServiceOwners": true,
279 | "customEmails": [ ]
280 | }
281 | }
282 | }
283 | ]
284 | }
--------------------------------------------------------------------------------
/ARM/azuredeploy.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "hostingPlanName": {
6 | "value": null
7 | },
8 | "location": {
9 | "value": "North Europe"
10 | },
11 | "webSiteName": {
12 | "value": null
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/DevOpsLab1.md:
--------------------------------------------------------------------------------
1 | # Lab 1: Creating the project
2 |
3 | ## Task 1: Create the Azure DevOps Team Project
4 |
5 | 1. Open a browser and navigate to your Azure DevOps organisation. The old URL is https://youraccountname.visualstudio.com and will work indefinitely or the new URL is https://dev.azure.com/yourorganisationname.
6 | 2. In your Azure DevOps organisation select Create Project.
7 |
8 | 3. Give the project a name, e.g. Web App. Make sure that version control is set to Git (select Advanced to show the options) and click Create. You could equally choose to use TFVC but this lab has documented the steps for using Git.
9 |
10 | 4. Your Azure DevOps team project has been created. You can add other people to the team if you want.
11 |
12 |
13 | ## Task 2: Cloning the Azure DevOps git repository to your local machine
14 |
15 | An empty git repository has been created in Azure DevOps and this step will clone that to a git repository on your local machine so that you can work locally and then push to Azure DevOps when required. There are different ways of doing this (command line, clone from Azure Repos in the browser) but here we will clone from within Visual Studio.
16 |
17 | 1. Launch Visual Studio. If you haven't used Visual Studio on the machine before you will be prompted to sign in when Visual Studio starts. Sign in with the account that you are using for Azure DevOps.
18 |
19 |
20 | 2. Open the Team Explorer view (usually right hand side of the IDE ina view next to the Solution Explorer) and select Manage Connections | Connect to Project.
21 |
22 |
23 | 3. Select the Web App project (you may only have one project listed) expand it to show the Web App repository and then click Clone.
24 |
25 |
26 | 4. If you now open the Team Explorer (bottom right hand corner) you will see that the repository has been cloned succesfully.
27 |
28 |
29 | ## Task 3: Creating the Web Application to deploy
30 |
31 | This step will create a sample web application using a standard Visual Studio template for simplicity.
32 |
33 | 1. Select New in the Solutions area of Team Explorer. This ensures that the app will be created in the correct location for the local git repo.
34 |
35 | 2. Select Web | ASP.NET Web Application, change the name as required and click OK.
36 |
37 | 3. Ensure that MVC is selected, disable Enable Docker if checked and check Add unit tests. Click OK.
38 |
39 | 4. When the project has been created, click the run button (green arrow) or F5 to build and launch the web application locally.
40 |
41 | 5. The web application will launch locally. Take a quick look and then close the browser.
42 |
43 | 6. To stop the debug session click the stop button or Shift + F5 in Visual Studio.
44 |
45 |
46 | You now have a web application. The next step is to add the application to Git so that it is under source control.
47 |
48 | ## Task 4: Committing the new Web App to source control
49 |
50 | 1. In the Visual Studio Team Explorer, select Changes.
51 |
52 | 2. You may be prompted to enter or confirm your Git User Information. Just click Save and you won't be prompted again.
53 |
54 | 3. Add a commit comment, such as Initial commit, then select Commit All and Push.
55 |
56 | 4. When completed you can go back to Azure DevOps in the browser, select Repos | Files and you will see your source code. Take a look at the history to see your initial commit.
57 |
58 |
59 | You now have a web application, committed to source control in Azure DevOps. The next step is to add an automated build that is triggered every time code is pushed to the repo - Continuous Integration.
60 |
61 | [<- Home](https://github.com/gidavies/WebAppDevOpsLab/blob/master/README.md) | [Lab 2: Continuous Integration ->](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab2.md)
--------------------------------------------------------------------------------
/DevOpsLab2.md:
--------------------------------------------------------------------------------
1 | # Lab 2: Continuous Integration
2 |
3 | Continuous Integration is a key DevOps practice to build, test and create the software to later deploy.
4 |
5 | ## Task 1: Set up the Continuous Integration definition
6 |
7 | 1. Navigate to the Azure DevOps repo, and select Set up build.
8 |
9 |
10 | 2. For this lab we will create the pipeline using the visual designer rather than YAML files. Click on the Use the visual designer link.
11 |
12 |
13 | 3. Choose the location for the git repo that the build pipeline will use as a source. In this case it is in Azure Repos, so you can keep all of the defaults and click Continue.
14 |
15 |
16 | 4. There are a range of build templates available, including non-Microsoft technologies but for this example select the ASP.NET template and click Apply.
17 |
18 |
19 | 5. The template creates a build definition with a number of tasks added. You can choose to run the builds using an on-premise agent or use the agents hosted on Azure. We will use the Hosted VS2017 agent as it has the .NET framework and all other components that are required to build the app. Check that the agent is set to Hosted 2017.
20 |
21 |
22 | 6. The template restores any dependencies using NuGet, builds the solution, runs any unit tests and then publishes the output. This should be ready to use, so for now test the build by clicking Save & Queue.
23 |
24 |
25 | 7. The next window allows you to change some inputs into the build, but just click Save & Queue.
26 |
27 |
28 | 8. You should now see that a build has been queued. Click on the build number to watch the build in progress.
29 |
30 |
31 | 9. Observe the build progressing. It will typically take a few minutes.
32 |
33 |
34 | 10. When the build completes click on the Artifacts drop down and select drop, which is the default output of the build
35 |
36 |
37 | 11. Expand the drop folder and notice that there is a zip file created from the build task in the build definition. This is the web application, packaged as a zip file, which is an easy way to deploy to Azure. Click Close.
38 |
39 | You now have a working build definition for the web application. The next step is to set it up with a Continuous Integration trigger and test it.
40 |
41 | ## Task 2: Enable Continuous Integration
42 |
43 | 1. Select Edit in the build result page.
44 |
45 |
46 | 2. Select Triggers and check Enable continuous integration. Save (but not queue).
47 |
48 |
49 | 3. Test the Continuous Integration trigger by returning to Visual Studio and making a change. For example open the WebApp/Views/Home/Index.cshtml and make a change such as changing the heading for the home page. Save the changes.
50 |
51 |
52 | 4. In the Team Explorer, return to the Changes hub to see the files you've changed, and add a comment and select Commit All and Push.
53 |
54 |
55 | 5. In Azure DevOps, navigate to the Builds (Pipelines | Builds) and you should now see a build in progress. If you want to click on the build to watch the build.
56 |
57 |
58 | You now have a build triggered whenever you make a change to the code and push that change to Git in Azure DevOps - Continuous Integration is in place for the project.
59 |
60 | >Optional challenge: Add a Build History widget to the Overview dashboard:
61 | >- Navigate to the dashboards and open the default overview dashboard
62 | >- Edit the dashboard and add the build history widget:
63 | >- Configure the widget to point to the build definition created in the preceding steps
64 |
65 | [<- Lab 1: Creating the project](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab1.md) | [Home](https://github.com/gidavies/WebAppDevOpsLab/blob/master/README.md) | [Lab 3: Create an Azure Web App ->](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab3.md)
--------------------------------------------------------------------------------
/DevOpsLab3.md:
--------------------------------------------------------------------------------
1 | # Lab 3: Create an Azure Web App
2 |
3 | [Azure Web Apps](https://docs.microsoft.com/en-gb/azure/app-service/app-service-web-overview) is an Azure service for hosting web applications. In this lab you'll create the Azure Web App into which you will later deploy the web application using Continuous Deployment.
4 |
5 | 1. In a browser go to the Azure Portal at http://portal.azure.com.
6 |
7 | 2. Select Create a resource and enter web app into the search field:
8 |
9 |
10 | 3. Press enter and select Web App from the list:
11 |
12 |
13 | 4. Click Create:
14 |
15 |
16 | 5. Complete the highlighted fields as follows:
17 |
18 | - App name: Choose a unique name that will be the URL for the web application such as WebApp plus your initials
19 |
20 | - Subscription: If you have more than one subscription, ensure that you choose the correct one for this lab
21 |
22 | - Resource Group: Create a new resource group for your web app
23 |
24 | - OS: Set this to Windows (as we will deploy a .NET web app later).
25 |
26 | - App Service Plan/Location: Click on this to create a new App Service Plan. Complete these fields:
27 | - App Service plan: Enter a name, such as WebAppPlan
28 | - Location: Select an Azure region close to you
29 | - Pricing tier: Click on this, and select the F1 Free tier
30 |
31 |
32 |
33 | 6. Click OK to save the App Service Plan.
34 |
35 | 7. Click Create to save and create the Web App.
36 |
37 | 8. After a short time (approx. 1-2 mins) you should see a notification that the Web App has been successfully created. You may want to pin the web app to your Azure dashboard for easy location later on:
38 |
39 |
40 |
41 | 9. Confirm that your Web App is created by selecting Go to resource.
42 |
43 | 10. Click on the URL:
44 |
45 |
46 | 11. Your Web App should open in the browser and you will see something like this:
47 |
48 |
49 | This confirms that you have created a Web App in Azure. In the next lab we will deploy the web application into the newly created Azure Web App.
50 |
51 | >Optional challenge: Add an Embedded Webpage widget to the Azure DevOps Overview dashboard:
52 | >- Add the Embedded Webpage widget
53 | >- Add the URL for your web app created in the preceding steps:
54 |
55 | [<- Lab 2: Continuous Integration](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab2.md) | [Home](https://github.com/gidavies/WebAppDevOpsLab/blob/master/README.md) | [Lab 4: Continuous Deployment ->](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab4.md)
--------------------------------------------------------------------------------
/DevOpsLab4.md:
--------------------------------------------------------------------------------
1 | # Lab 4: Continuous Deployment
2 |
3 | Continuous Deployment is another key practice within DevOps to enable the continuous delivery of value (in this example the web application) to end users.
4 |
5 | ## Task 1: Create the release pipeline
6 |
7 | 1. In the Builds hub select the latest build, then click on the 3 dots and select Create release.
8 |
9 |
10 | 2. Select a template for the release pipeline. If you scroll through the list you'll see that there are many templates. As we are going to deploy a web application into an Azure App Service, select the Azure App Service Deployment template and click Apply.
11 |
12 |
13 | 3. A release pipeline may have many stages such as Dev, QA, UAT, pre-prod, prod. For now we will have one, and the first stage to deploy to is typically a shared development environment, so call it Dev or similar. Then click the close button.
14 |
15 |
16 | 4. Notice that there is already an Artifact to deploy. This has been configured automatically as the release was created from the build. Click on the Artifact to see the details.
17 |
18 |
19 | 5. This shows that the Artifact is the latest version of the output of the CI build that was created earlier. That output includes the zip file, which is the web application to be deployed. Close the window.
20 |
21 |
22 | 6. Click on the Artifact Continuous deployment trigger.
23 |
24 |
25 | 7. This is where you can enable or disable Continuous Deployment - i.e. whenever a new build is created, this release pipeline is triggered. Notice that it should already be set to Enabled (set it if not). Close the window.
26 |
27 |
28 | 8. Now click on the jobs and tasks in the Dev environment. This is where we will configure how to do the deployment into Azure.
29 |
30 |
31 | 9. The first areas to address in the environment are Azure settings.
32 |
33 |
34 | 10. In the Azure subscriptions list, choose the relevant subscription and click Authorize. This is to create an authorised connection to Azure using that subscription.
35 |
36 |
37 | 11. Having authorised the subscription you should be able to see and select the Web App that you created in the preceding step. This is the target Web App that we will deploy to.
38 |
39 |
40 | 12. Click the Run on agent step and confirm that the agent is set to the same as the build - the Hosted VS2017 agent.
41 |
42 |
43 | 13. Click Deploy Azure App Service task. You shouldn't need to change anything here but note that this uses the Azure subscription to deploy to the App Service, and the package to be deployed is a zip file, as created in the CI build. Click Save and accept the default location for the pipeline.
44 |
45 |
46 | You have now created a Release Pipeline, configured to Continuously Deploy whenever there is a new build. The next step is to test the overall flow.
47 |
48 | ## Task 2: Test the release pipeline
49 |
50 | 1. Test the release pipeline by returning to Visual Studio and making a change. For example open the WebApp/Views/Home/Index.cshtml again and make another change such as changing the heading for the home page. Save the changes.
51 |
52 |
53 | 2. In the Team Explorer, return to the Changes hub (click the Home icon if needed to return to the hubs) to see the files you've changed, and add a comment and select Commit All and Push.
54 |
55 |
56 | 3. In Azure DevOps, navigate to the Builds (Pipelines | Builds) and you should now see a build in progress. Click on the build number to watch the build.
57 |
58 |
59 | 4. Open the build log and when it's completed select the summary and scroll down to see the Deployments area. You should see that a release to the Dev stage is in progress and therefore Continuous Deployment has been successfully triggered.
60 |
61 |
62 | 5. Click on the Release link (it should be Release-1) in Deployments to see the release pipeline. Hover over the Dev stage and select Logs if you want to look at the detailed logs. Note that the zip file (artifact) is downloaded from the build (not rebuilt) and then deployed to Azure.
63 |
64 |
65 | 6. Go to the web app URL (as per Lab 3 Step 10 above) and refresh the page to see your newly deployed web application.
66 |
67 |
68 | You have now deployed a web application into a live Azure site using a DevOps release pipeline triggered by committing a code change. If you want, make one or more other changes in the code, commit and push and see those changes built and deployed into your web application.
69 |
70 | >Optional: Add a Release Definition Overview widget to the Overview dashboard by:
71 | >- Add a Release Definition Overview widget.
72 | >- In the configuration widget set the release definition to the release created in the lab above.
73 | >- Save the changesto the dashboard.
74 |
75 | [<- Lab 3: Create an Azure Web App](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab3.md) | [Home](https://github.com/gidavies/WebAppDevOpsLab/blob/master/README.md) | [Lab 5: Infrastructure as Code ->](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab5.md)
--------------------------------------------------------------------------------
/DevOpsLab5.md:
--------------------------------------------------------------------------------
1 | # Lab 5: Infrastructure as Code
2 |
3 | The ability to treat infrastructure (machines, networks, configuration) in the same way as code brings many benefits, but in particular allows you to create infrastructure on demand and include that in your DevOps pipeline.
4 |
5 | Azure Resource Manager (ARM) templates are the native approach and this lab adds using ARM into the flow. The example here will allow you to create an environment in Azure on demand as part of the flow.
6 |
7 | This lab will create a new test environment in Azure without needing to manually create it (via the Portal or the Command line etc.).
8 |
9 | ## Task 1 - Create the ARM template for a Web App
10 |
11 | 1. In Visual Studio, with the Web App solution open in Solution Explorer, right click the solution and select Add | New Project.
12 |
13 |
14 | 2. Select Cloud | Azure Resource Group and name the project e.g. WebApp.ARM.
15 |
16 |
17 | 3. There are a range of ARM templates to create a wide variety or resources in Azure. In this case from the Visual Studio Templates select Blank Template and click OK.
18 |
19 |
20 | 4. You now have a project in your solution containing a blank ARM template (azuredeploy.json) and a blank parameters file (azuredeploy.parameters.json).
21 |
22 |
23 | 5. Now replace the content of the files created in the previous step with pre-prepared content:
24 | - View (or download) [this azuredeploy.json file](https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/master/ARM/azuredeploy.json), select all the contents of the file, and then copy and paste over the content of the azuredeploy.json file in Visual Studio.
25 | - Do the same with this [azuredeploy.parameters.json file](https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/master/ARM/azuredeploy.parameters.json).
26 | - Save the files in Visual Studio.
27 |
28 | 6. In Visual Studio, select the Team Explorer | Changes. Add a commit comment and select Commit All and Push. Save if prompted.
29 |
30 | The ARM template is now added to source control, although there is no need for it to be in the same repository as the code, this is just for convenience in this lab. Note that adding the new files to source control will trigger a build and release in the background. You could temporarily turn off the CI trigger but just let it run in the background while completing the next task.
31 |
32 | ## Task 2 - Update the release pipeline to provision the Web App using the ARM template.
33 |
34 | 1. In Azure DevOps select Pipelines | Releases | select your release definition and Edit pipeline.
35 |
36 |
37 | 2. Add a new artifact to the release pipeline. This means the release will get the application from the build (as a zip file) and the ARM templates directly from source control (as they don't need to be compiled or packaged). Set the Source type to Azure Repo, the Project and Source (repository) to the Web App project, the Default branch to master and the default version to Latest from default branch. Then click Add (you may need to scroll down).
38 |
39 |
40 | 3. In the Pipeline view hover over the Dev stage and select Clone.
41 |
42 |
43 | 4. Select the cloned stage Copy of Dev and change the name to QA and close the Stage window.
44 |
.
45 |
46 | 5. Select the QA stage (by clicking on the "1 job, 1 task" link inside the QA environment) and in Tasks click the plus sign by Run on Agent and then search for the Azure Resource Group Deployment task. Select Add.
47 |
48 |
49 | 6. Set the Azure Details. Select the Azure subscription set up previously, Ensure that the Action is Create or update resource group, enter a new resource group name for the QA environment e.g. WebAppQA-RG and set the location.
50 |
51 |
52 | 7. In the Template section set the Template (using the ... button) to Web App (Azure Repos Git) WebApp/WebApp.ARM/azuredeploy.json and click OK.
53 |
54 |
55 | 8. Set the Template parameters field (using the ... button) to WebApp (Git) WebApp.ARM/azuredeploy.parameters.json and click OK.
56 |
57 |
58 | 9. Set the Override Template parameters field (using the ... button) to WebAppQAPlan, the webSiteName to the same as the Dev website but with QA appended (e.g. WebApp-GJAD-QA)and click OK.
59 |
60 |
61 | 10. Move the Azure Deployment: Create or Update Resource Group task to be before the Deploy Azure App Service task by dragging and dropping the task.
62 |
63 | 11. In the QA deployment process settings set the App service name to the name you used in step 9 above. Save your changes.
64 |
65 |
66 | 12. Test the changes by making another code change in Visual Studio(e.g. changing the heading again), committing and pushing, and observe the build and release.
67 | After a few minutes you should see that both the Dev and QA stages have been successfully deployed to.
68 |
69 |
70 | 13. Explore the [Azure portal](http://portal.azure.com) to find the resource group WebApp-QA-RG and the web app provisioned using ARM in the QA environment. Confirm that the web application has been deployed and open it using the URL in the App service overview.
71 |
72 |
73 | You have now created a DevOps pipeline that deploys to multiple stages, and provisions the QA stage on demand without a manual process. You could now delete the QA web app in the Azure portal and run the release pipeline again and it will recreate the QA web app.
74 |
75 | [<- Lab 4: Continuous Deployment](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab4.md) | [Home](https://github.com/gidavies/WebAppDevOpsLab/blob/master/README.md) | [Lab 6: Automated Testing with Selenium ->](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab6.md)
--------------------------------------------------------------------------------
/DevOpsLab6.md:
--------------------------------------------------------------------------------
1 | # Lab 6: Automated Testing with Selenium
2 |
3 | Integrating automated tests into your DevOps pipeline can help drive quality whilst deploying more frequently. This lab integrates [Selenium](http://www.seleniumhq.org/), a popular automated functional testing framework, into your pipeline. Selenium allows you to automate the testing of your web application using all the main browsers, reducing the manual testing required.
4 |
5 | Selenium drivers are already installed on the Hosted agents being used in this pipeline. You can use the drivers to run tests in IE, Chrome or Firefox. For this example Chrome will be used, but you could follow the same steps and switch the driver to another browser, or repeat tests using multiple browsers.
6 |
7 | ## Task 1: Create the Selenium tests
8 |
9 | 1. In Visual Studio, Solution Explorer, select the solution and Add | New Project. Then select the Test category and Unit Test Project. Give the project a name, such as WebApp.UITest, and click OK.
10 |
11 |
12 | 2. The Selenium libraries need to be added to the project. Right-click on the test project and select Manage NuGet Packages.
13 |
14 |
15 | 3. Select Browse, search for Selenium and install the latest (default) versions of Selenium.WebDriver and Selenium.Chrome.WebDriver
16 |
17 |
18 | 4. Close the NuGet page.
19 |
20 | 5. Replace the entire contents of UnitTest1.cs with the following:
21 |
22 | ```c#
23 | using System;
24 | using Microsoft.VisualStudio.TestTools.UnitTesting;
25 | using OpenQA.Selenium.Remote;
26 | using OpenQA.Selenium.Chrome;
27 |
28 | namespace WebApp.UITest
29 | {
30 | [TestClass]
31 | public class UITests
32 | {
33 | private static RemoteWebDriver _webDriver = null;
34 | private static string _webAppBaseURL;
35 |
36 | [ClassInitialize()]
37 | public static void ClassInit(TestContext context)
38 | {
39 | // The hosted agent env variable for IE is IEWebDriver, Firefox is GeckoWebDriver
40 | // If you switch the driver you'll need the corresponding nuget package.
41 | _webDriver = new ChromeDriver(Environment.GetEnvironmentVariable("ChromeWebDriver"));
42 |
43 | // Allow for web app compilation and startup post deployment
44 | _webDriver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(15);
45 |
46 | _webAppBaseURL = "https://.azurewebsites.net/";
47 | }
48 |
49 | [ClassCleanup()]
50 | public static void Cleanup()
51 | {
52 | if (_webDriver != null)
53 | {
54 | _webDriver.Quit();
55 | }
56 | }
57 |
58 | [TestMethod]
59 | [TestCategory("Selenium")]
60 | public void HomePageFoundTest()
61 | {
62 | _webDriver.Url = _webAppBaseURL;
63 |
64 | string actualPageTitle = _webDriver.Title;
65 | string expectedPageTitle = "Home Page - My ASP.NET Application";
66 |
67 | Assert.AreEqual(expectedPageTitle, actualPageTitle);
68 | }
69 |
70 | [TestMethod]
71 | [TestCategory("Selenium")]
72 | public void AboutPageFoundTest()
73 | {
74 | _webDriver.Url = _webAppBaseURL + "/Home/About";
75 |
76 | string actualPageTitle = _webDriver.Title;
77 | string expectedPageTitle = "About - My ASP.NET Application";
78 |
79 | Assert.AreEqual(expectedPageTitle, actualPageTitle);
80 | }
81 |
82 | [TestMethod]
83 | [TestCategory("Selenium")]
84 | public void ContactPageFoundTest()
85 | {
86 | _webDriver.Url = _webAppBaseURL + "/Home/Contact";
87 |
88 | string actualPageTitle = _webDriver.Title;
89 | string expectedPageTitle = "Contact - My ASP.NET Application";
90 |
91 | Assert.AreEqual(expectedPageTitle, actualPageTitle);
92 | }
93 |
94 | [TestMethod]
95 | [TestCategory("Selenium")]
96 | public void SupportEmailAddressChromeTest()
97 | {
98 | string supportEmailAddress = "Support@example.com";
99 |
100 | _webDriver.Url = _webAppBaseURL + "/Home/Contact";
101 | RemoteWebElement supportEmailElement = (RemoteWebElement)_webDriver.FindElementByLinkText(supportEmailAddress);
102 |
103 | Assert.AreEqual(supportEmailAddress, supportEmailElement.Text);
104 | }
105 |
106 | [TestMethod]
107 | [TestCategory("Selenium")]
108 | public void MarketingEmailAddressTest()
109 | {
110 | string marketingEmailAddress = "Marketing@example.com";
111 |
112 | _webDriver.Url = _webAppBaseURL + "/Home/Contact";
113 | RemoteWebElement marketingEmailElement = (RemoteWebElement)_webDriver.FindElementByLinkText(marketingEmailAddress);
114 |
115 | Assert.AreEqual(marketingEmailAddress, marketingEmailElement.Text);
116 | }
117 |
118 | [TestMethod]
119 | [TestCategory("Selenium")]
120 | public void IndexTitleTest()
121 | {
122 | string expectedTitle = "Your heading";
123 |
124 | _webDriver.Url = _webAppBaseURL + "/Home/Index";
125 | RemoteWebElement titleElement = (RemoteWebElement)_webDriver.FindElementByTagName("H1");
126 |
127 | Assert.AreEqual(expectedTitle, titleElement.Text);
128 | }
129 | }
130 | }
131 | ```
132 | 6. This code contains six tests that will be executed against the web application using the Selenium PhantomJS driver. The tests check that pages and content on those pages exist. Note:
133 | - Change the namespace if you used a different name.
134 | - You need to add your QA web app url into line 24 (the one you can find as per the last step in Lab 5 above):
135 | ```c
136 | _webAppBaseURL = "https://.azurewebsites.net/";
137 | ```
138 | - The final test (IndexTitleTest) checks the heading on the home page that you have been changing in the previous labs. Make sure that the expected value is correct for your web app (line 100).
139 | ```c
140 | string expectedTitle = "Your heading";
141 | ```
142 | - The [TestCategory] property which will be used later to filter which tests to run
143 |
144 | 7. Save all files.
145 |
146 | You now have some Selenium tests in the project. Before committing these to source control the next step is to amend the Azure DevOps pipeline to incorporate the tests into the flow.
147 |
148 | ## Task 2: Add the tests into the pipeline
149 |
150 | 1. First the build definition needs to ensure that the tests are available in the build output, so that they can be run in the release. Open the build definition (Pipelines | Builds | Edit).
151 |
152 | 2. In Agent Job 1, click + select the Utility tab and select the Copy Files task. This task will ensure that the tests are avaiable in the build output to be used in the release.
153 |
154 |
155 | 3. Set the following values in the Copy Files task:
156 | - Source Folder: $(build.sourcesdirectory)
157 | - Contents: \*\*\bin\\$(BuildConfiguration)\\*\*
158 | - Target Folder: $(build.artifactstagingdirectory)
159 | - The Display name will update as you make changes.
160 |
161 |
162 | 4. Move (drag and drop) the Copy Files task to be immediately after the Build solution task. The compiled tests will now be available in the build output the next time the build runs, and therefore availble to the release pipeline.
163 |
164 | 5. In this example we want the CI build to continue to only run unit tests and not the new Selenium tests, which require a deployed application. Therefore update the Test Assemblies task and add TestCategory!=Selenium in the Test filter criteria field. This uses the TestCategories in the test code to filter the tests to be run, in this case excluding the Selenium tests.
165 |
166 |
167 | 6. Save (but not queue) the build.
168 |
169 | 7. The next step is to execute the tests as part of the release. Open the Release definition (Pipelines | Releases | Edit) and click on the tasks for the QA environment. Click the + button, select the Test tab and select the Visual Studio Test task. The Selenium tests are within a unit test so this task can execute them.
170 |
171 |
172 | 8. Select the test task. In the Test filter criteria field add TestCategory=Selenium. This will only execute tests that have the matching [TestCategory] property in the code and provides control over which tests to run. In this example we have decided not to run the Selenium tests in the CI build but want to run them against the deployed application in the QA environment.
173 |
174 |
175 | 9. Ensure that the test task is the last task and save the release.
176 |
177 | 10. Now return to Visual Studio and the Team Explorer. You may need to click the back arrow or home button in Team Explorer. Then commit and push the changes. This will trigger the CI build, which will now include the tests in the output, and then trigger the CD release, which will execute the tests found in the build output. Wait for the build and release into QA to complete and then open the release pipeline summary. You should see that there are tests in the QA environment and that they have passed.
178 |
179 |
180 | 11. Click on the 100% pass link for the QA environment and you will see the individual test results. By default the view is filtered to only show failed tests, select Outcomes and clear the filters to see the individual test results.
181 |
182 |
183 | You now have automated UI tests being executed everytime a new version of your application is deployed.
184 |
185 | >Optional: You might want to see what happens when a test fails. You could change the heading for the web app again, but not update the tests (or the other way around), and commit the change. You should then see a successful CI build (as the UI Tests aren't run in the build), a successful release to Dev (as the Selenium tests aren't run in that stage) but a failed release to QA, as that's the stage the Selenium tests are run in.
186 |
187 | >Optional: Add a Deployment status widget showing test results to the Lab Progress dashboard by:
188 | >- Searching for and adding the Deployment status widget.
189 | >- Configure the widget for the build and release pipelines including all environments.
190 |
191 | [<- Lab 5: Infrastructure as Code](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab5.md) | [Home](https://github.com/gidavies/WebAppDevOpsLab/blob/master/README.md) | [Lab 7: Monitoring with Application Insights ->](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab7.md)
--------------------------------------------------------------------------------
/DevOpsLab7.md:
--------------------------------------------------------------------------------
1 | # Lab 7: Monitoring with Application Insights
2 |
3 | DevOps doesn't stop at deployment into production. Monitoring and understanding your application provides valuble insight.
4 |
5 | 1. In Visual Studio right-click on the WebApp project | Application Insights | Configure Application Insights
6 |
7 |
8 | 2. Update the SDK, if needed, and click Start Free.
9 |
10 |
11 | 3. Set the correct account, subscription and resource - for resource that will probably be a New resource. Also decide whether you want Application Insights to be capped at the free tier or allow it to exceed the free tier limits. If in doubt select to halt collection at the free limits.
12 |
13 |
14 | 4. Commit and push the changes. This will trigger another build and release.
15 |
16 |
17 | 5. When the release to QA has completed go to the [Azure portal](http://portal.azure.com) select the Application Insights service for the Web App (it may take a little while for data to show). Click on the Live Stream button.
18 |
19 |
20 | 6. Give it a few seconds for some baseline data to show.
21 |
22 |
23 | 7. Go to your deployed app and click on the Home, About and Contact pages, then return to the Live Stream to see the data showing.
24 |
25 |
26 | 8. Close the Live Stream and click on the Page View Load Time graph.
27 |
28 |
29 | 9. Take a look at data on page response time
30 |
.
31 |
32 | 10. Close the Page response time blade and select Analytics.
33 |
34 |
35 | 11. Try some of the queries such as the Users query
36 |
37 |
38 |
39 | This is just a tiny sample of what can be done with Application Insights, you can [read more here](https://docs.microsoft.com/en-us/azure/application-insights/)
40 |
41 | >Optional: Add an Application Insights widget showing test results to the Lab Progress dashboard by:
42 | >- Find and install the Application Insights widget in the [marketplace](http://marketplace.visualstudio.com/)
43 | >
44 |
45 | >- Searching for and adding the Application Insights Chart (and/or Metrics) widget:
46 | >
47 |
48 | >- Configure the widget following [these instructions](https://marketplace.visualstudio.com/items?itemName=ms-appinsights.ApplicationInsightsWidgets)
49 | >
50 |
51 | >- Save the changes, close the widget gallery and save the dashboard by clicking on the blue edit button in the bottom right hand corner.
52 |
53 | This completes the DevOps labs.
54 |
55 | [<- Lab 6: Automated Testing with Selenium](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab6.md) | [Home](https://github.com/gidavies/WebAppDevOpsLab/blob/master/README.md)
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Web App DevOps Lab
2 |
3 | This lab will step through the key elements in setting up a DevOps pipeline using Azure DevOps, previously known as Visual Studio Team Services (VSTS). What was VSTS has now been separated into a suite of tooling including Azure Repos and Azure Pipelines which will be primarily used in this lab.
4 |
5 | >Background information
6 |
7 | >[Understanding the change from VSTS to Azure DevOps](https://azure.microsoft.com/en-us/blog/introducing-azure-devops/)
8 |
9 | >[What is DevOps?](https://www.visualstudio.com/learn/what-is-devops/)
10 |
11 | >[How Microsoft does DevOps](https://www.visualstudio.com/learn/devops-at-microsoft/)
12 |
13 | # Overall flow
14 |
15 | - [Lab 1: Creating the project](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab1.md)
16 | - [Lab 2: Continuous Integration](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab2.md)
17 | - [Lab 3: Create an Azure Web App](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab3.md)
18 | - [Lab 4: Continuous Deployment](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab4.md)
19 | - [Lab 5: Infrastructure as Code](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab5.md)
20 | - [Lab 6: Automated Testing with Selenium](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab6.md)
21 | - [Lab 7: Monitoring with Application Insights](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab7.md)
22 |
23 | Azure DevOps supports any app and doesn't require the use of Visual Studio, .NET or other Microsoft languages or platforms. [Labs that work through implementing DevOps with Node, Java, Eclipse, IntelliJ, Docker and more are available here](https://www.azuredevopslabs.com/).
24 |
25 | # Other tasks
26 |
27 | This lab outlines the key practices in implementing a DevOps pipeline but there are many other tasks that could be added, and you may wish to look into as additional steps after the lab:
28 | - Setting pre and post approvals on release environments
29 | - Using variables in releases across environment
30 | - Adding load testing to the flow
31 | - Linking changes to user stories and other work items in Azure Boards to understand what has been built and released.
32 | - Using Git branches and merging via Pull Requests in Azure Repos
33 |
34 | # Preparing for the lab
35 |
36 | For this lab you will require:
37 |
38 | - An Azure DevOps organisation (formerly known as a VSTS account).
39 | - An Azure subscription (your own or a free trial).
40 | - Visual Studio 2017 (any edition) installed.
41 |
42 | Use the same account (login/email and password) for both Azure DevOps and Visual Studio.
43 |
44 | If you don't have one create a free [Azure DevOps organisation](http://dev.azure.com). [Guidance for creating an organisation](https://docs.microsoft.com/en-us/azure/devops/user-guide/sign-up-invite-teammates?view=vsts).
45 |
46 | # Using the New Navigation UI
47 |
48 | In order to have a single flow this lab assumes that you will use the New Navigation UI that is in preview in Azure DevOps. If you create a new organisation it will be on by default, if you have an existing one then to match the labs you will need to enable this preview feature (and you can disable it after the lab to return to the old UI if preferred).
49 |
50 | To enable the New Navigation follow these steps:
51 |
52 | 1. In Azure DevOps, in the top right hand corner click on your profile picture (or initials if no photo), and then select Preview features.
53 |
54 |
55 | 2. Turn on the toggle for the New Navigation and click on the X close button to close the preview features
56 |
57 |
58 | The UI should then reload and you will be using the New Navigation. To turn off the New Navigation repeat the above and untoggle the feature.
59 |
60 | [Lab 1: Creating the project ->](https://github.com/gidavies/WebAppDevOpsLab/blob/master/DevOpsLab1.md)
61 |
62 |
--------------------------------------------------------------------------------
/images/AI_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/AI_1.png
--------------------------------------------------------------------------------
/images/AI_10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/AI_10.png
--------------------------------------------------------------------------------
/images/AI_11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/AI_11.png
--------------------------------------------------------------------------------
/images/AI_12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/AI_12.png
--------------------------------------------------------------------------------
/images/AI_13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/AI_13.png
--------------------------------------------------------------------------------
/images/AI_14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/AI_14.png
--------------------------------------------------------------------------------
/images/AI_15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/AI_15.png
--------------------------------------------------------------------------------
/images/AI_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/AI_2.png
--------------------------------------------------------------------------------
/images/AI_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/AI_3.png
--------------------------------------------------------------------------------
/images/AI_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/AI_4.png
--------------------------------------------------------------------------------
/images/AI_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/AI_5.png
--------------------------------------------------------------------------------
/images/AI_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/AI_6.png
--------------------------------------------------------------------------------
/images/AI_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/AI_7.png
--------------------------------------------------------------------------------
/images/AI_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/AI_8.png
--------------------------------------------------------------------------------
/images/AI_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/AI_9.png
--------------------------------------------------------------------------------
/images/CD_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_1.png
--------------------------------------------------------------------------------
/images/CD_10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_10.png
--------------------------------------------------------------------------------
/images/CD_11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_11.png
--------------------------------------------------------------------------------
/images/CD_12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_12.png
--------------------------------------------------------------------------------
/images/CD_13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_13.png
--------------------------------------------------------------------------------
/images/CD_14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_14.png
--------------------------------------------------------------------------------
/images/CD_15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_15.png
--------------------------------------------------------------------------------
/images/CD_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_16.png
--------------------------------------------------------------------------------
/images/CD_17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_17.png
--------------------------------------------------------------------------------
/images/CD_18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_18.png
--------------------------------------------------------------------------------
/images/CD_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_2.png
--------------------------------------------------------------------------------
/images/CD_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_3.png
--------------------------------------------------------------------------------
/images/CD_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_4.png
--------------------------------------------------------------------------------
/images/CD_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_5.png
--------------------------------------------------------------------------------
/images/CD_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_6.png
--------------------------------------------------------------------------------
/images/CD_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_7.png
--------------------------------------------------------------------------------
/images/CD_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_8.png
--------------------------------------------------------------------------------
/images/CD_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/CD_9.png
--------------------------------------------------------------------------------
/images/IC_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_1.png
--------------------------------------------------------------------------------
/images/IC_10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_10.png
--------------------------------------------------------------------------------
/images/IC_11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_11.png
--------------------------------------------------------------------------------
/images/IC_12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_12.png
--------------------------------------------------------------------------------
/images/IC_13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_13.png
--------------------------------------------------------------------------------
/images/IC_14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_14.png
--------------------------------------------------------------------------------
/images/IC_15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_15.png
--------------------------------------------------------------------------------
/images/IC_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_16.png
--------------------------------------------------------------------------------
/images/IC_17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_17.png
--------------------------------------------------------------------------------
/images/IC_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_2.png
--------------------------------------------------------------------------------
/images/IC_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_3.png
--------------------------------------------------------------------------------
/images/IC_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_4.png
--------------------------------------------------------------------------------
/images/IC_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_5.png
--------------------------------------------------------------------------------
/images/IC_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_6.png
--------------------------------------------------------------------------------
/images/IC_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_7.png
--------------------------------------------------------------------------------
/images/IC_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_8.png
--------------------------------------------------------------------------------
/images/IC_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/IC_9.png
--------------------------------------------------------------------------------
/images/Intro1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Intro1.png
--------------------------------------------------------------------------------
/images/Intro2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Intro2.png
--------------------------------------------------------------------------------
/images/L1_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_1.png
--------------------------------------------------------------------------------
/images/L1_10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_10.png
--------------------------------------------------------------------------------
/images/L1_11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_11.png
--------------------------------------------------------------------------------
/images/L1_12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_12.png
--------------------------------------------------------------------------------
/images/L1_13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_13.png
--------------------------------------------------------------------------------
/images/L1_14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_14.png
--------------------------------------------------------------------------------
/images/L1_14a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_14a.png
--------------------------------------------------------------------------------
/images/L1_15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_15.png
--------------------------------------------------------------------------------
/images/L1_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_16.png
--------------------------------------------------------------------------------
/images/L1_17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_17.png
--------------------------------------------------------------------------------
/images/L1_18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_18.png
--------------------------------------------------------------------------------
/images/L1_19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_19.png
--------------------------------------------------------------------------------
/images/L1_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_2.png
--------------------------------------------------------------------------------
/images/L1_20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_20.png
--------------------------------------------------------------------------------
/images/L1_21.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_21.png
--------------------------------------------------------------------------------
/images/L1_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_3.png
--------------------------------------------------------------------------------
/images/L1_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_4.png
--------------------------------------------------------------------------------
/images/L1_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_5.png
--------------------------------------------------------------------------------
/images/L1_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_6.png
--------------------------------------------------------------------------------
/images/L1_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_7.png
--------------------------------------------------------------------------------
/images/L1_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_8.png
--------------------------------------------------------------------------------
/images/L1_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_9.png
--------------------------------------------------------------------------------
/images/L1_A1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_A1.png
--------------------------------------------------------------------------------
/images/L1_A2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_A2.png
--------------------------------------------------------------------------------
/images/L1_A3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L1_A3.png
--------------------------------------------------------------------------------
/images/L2_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_1.png
--------------------------------------------------------------------------------
/images/L2_10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_10.png
--------------------------------------------------------------------------------
/images/L2_11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_11.png
--------------------------------------------------------------------------------
/images/L2_12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_12.png
--------------------------------------------------------------------------------
/images/L2_13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_13.png
--------------------------------------------------------------------------------
/images/L2_14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_14.png
--------------------------------------------------------------------------------
/images/L2_15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_15.png
--------------------------------------------------------------------------------
/images/L2_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_2.png
--------------------------------------------------------------------------------
/images/L2_22.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_22.png
--------------------------------------------------------------------------------
/images/L2_23.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_23.png
--------------------------------------------------------------------------------
/images/L2_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_3.png
--------------------------------------------------------------------------------
/images/L2_31.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_31.png
--------------------------------------------------------------------------------
/images/L2_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_4.png
--------------------------------------------------------------------------------
/images/L2_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_5.png
--------------------------------------------------------------------------------
/images/L2_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_6.png
--------------------------------------------------------------------------------
/images/L2_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_7.png
--------------------------------------------------------------------------------
/images/L2_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_8.png
--------------------------------------------------------------------------------
/images/L2_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/L2_9.png
--------------------------------------------------------------------------------
/images/Lab1_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab1_1.png
--------------------------------------------------------------------------------
/images/Lab1_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab1_2.png
--------------------------------------------------------------------------------
/images/Lab1_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab1_3.png
--------------------------------------------------------------------------------
/images/Lab1_T4_S4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab1_T4_S4.png
--------------------------------------------------------------------------------
/images/Lab2_T1_S1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab2_T1_S1.png
--------------------------------------------------------------------------------
/images/Lab2_T1_S10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab2_T1_S10.png
--------------------------------------------------------------------------------
/images/Lab2_T1_S2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab2_T1_S2.png
--------------------------------------------------------------------------------
/images/Lab2_T1_S3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab2_T1_S3.png
--------------------------------------------------------------------------------
/images/Lab2_T1_S9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab2_T1_S9.png
--------------------------------------------------------------------------------
/images/Lab2_T2_S1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab2_T2_S1.png
--------------------------------------------------------------------------------
/images/Lab2_T2_S5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab2_T2_S5.png
--------------------------------------------------------------------------------
/images/Lab4_T1_S1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab4_T1_S1.png
--------------------------------------------------------------------------------
/images/Lab4_T1_S3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab4_T1_S3.png
--------------------------------------------------------------------------------
/images/Lab4_T1_S8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab4_T1_S8.png
--------------------------------------------------------------------------------
/images/Lab4_T2_S3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab4_T2_S3.png
--------------------------------------------------------------------------------
/images/Lab4_T2_S4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab4_T2_S4.png
--------------------------------------------------------------------------------
/images/Lab4_T2_S5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab4_T2_S5.png
--------------------------------------------------------------------------------
/images/Lab5_T2_S1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab5_T2_S1.png
--------------------------------------------------------------------------------
/images/Lab5_T2_S12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab5_T2_S12.png
--------------------------------------------------------------------------------
/images/Lab5_T2_S2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab5_T2_S2.png
--------------------------------------------------------------------------------
/images/Lab5_T2_S3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab5_T2_S3.png
--------------------------------------------------------------------------------
/images/Lab5_T2_S4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab5_T2_S4.png
--------------------------------------------------------------------------------
/images/Lab6_T1_S3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab6_T1_S3.png
--------------------------------------------------------------------------------
/images/Lab6_T2_S10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab6_T2_S10.png
--------------------------------------------------------------------------------
/images/Lab6_T2_S11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab6_T2_S11.png
--------------------------------------------------------------------------------
/images/Lab6_T2_S2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab6_T2_S2.png
--------------------------------------------------------------------------------
/images/Lab6_T2_S5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab6_T2_S5.png
--------------------------------------------------------------------------------
/images/Lab6_T2_S8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/Lab6_T2_S8.png
--------------------------------------------------------------------------------
/images/S_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/S_1.png
--------------------------------------------------------------------------------
/images/S_10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/S_10.png
--------------------------------------------------------------------------------
/images/S_11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/S_11.png
--------------------------------------------------------------------------------
/images/S_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/S_2.png
--------------------------------------------------------------------------------
/images/S_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/S_3.png
--------------------------------------------------------------------------------
/images/S_3_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/S_3_1.png
--------------------------------------------------------------------------------
/images/S_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/S_4.png
--------------------------------------------------------------------------------
/images/S_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/S_5.png
--------------------------------------------------------------------------------
/images/S_5_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/S_5_1.png
--------------------------------------------------------------------------------
/images/S_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/S_6.png
--------------------------------------------------------------------------------
/images/S_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/S_7.png
--------------------------------------------------------------------------------
/images/S_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/S_8.png
--------------------------------------------------------------------------------
/images/S_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/S_9.png
--------------------------------------------------------------------------------
/images/WA-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/WA-1.png
--------------------------------------------------------------------------------
/images/WA-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/WA-2.png
--------------------------------------------------------------------------------
/images/WA-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/WA-3.png
--------------------------------------------------------------------------------
/images/WA-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/WA-4.png
--------------------------------------------------------------------------------
/images/WA-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/WA-5.png
--------------------------------------------------------------------------------
/images/WA-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/WA-6.png
--------------------------------------------------------------------------------
/images/WA-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/WA-7.png
--------------------------------------------------------------------------------
/images/WA-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/WA-8.png
--------------------------------------------------------------------------------
/images/WA-9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gidavies/WebAppDevOpsLab/0752eb6e2c741dead7e1da5c55d468044de360e4/images/WA-9.png
--------------------------------------------------------------------------------